source: trunk/modules/sdmmc/caba/source/src/sdmmc.cpp @ 574

Last change on this file since 574 was 574, checked in by bouyer, 10 years ago

Try to be closer to a real sdmmc device: on rising edge really use the value
we had while the clock was low, not the value at the rising edge time.
This is to make the transfer fail if the MOSI line changes at rising clock
instead of falling clock as it should.

File size: 14.7 KB
RevLine 
[555]1/*
2 -*- c++ -*-
[552]3 *
4 * SOCLIB_LGPL_HEADER_BEGIN
5 *
6 * This file is part of SoCLib, GNU LGPLv2.1.
7 *
8 * SoCLib is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published
10 * by the Free Software Foundation; version 2.1 of the License.
11 *
12 * SoCLib is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with SoCLib; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 *
22 * SOCLIB_LGPL_HEADER_END
23 *
24 * Copyright (c) UPMC, Lip6, SoC
25 *         manuel.bouyer@lip6.fr october 2013
26 *
27 * Maintainers: bouyer
28 */
29
30#include <stdint.h>
31#include <errno.h>
32#include <iostream>
33#include <fcntl.h>
34#include <cassert>
35#include "sdmmc.h"
36
37namespace soclib { namespace caba {
38
39using namespace soclib::caba;
40
41////////////////////////
[557]42void SdMMC::genMealy()
[552]43{
44    if(p_resetn.read() == false) 
45    {
[556]46        spi_fsm  = S_IDLE;
[552]47        m_acmd     = false;     
48        m_sdstate  = SD_IDLE;
49        return;
50    } 
51    if (p_spi_ss.read()) {
[556]52        if (spi_fsm != S_IDLE) {
[555]53                std::cerr << name() << " deselect but not idle, state "
[556]54                << std::dec << spi_fsm << " last cmd " << (int)command
55                << " args " << std::hex << args << std::dec
56                << " bitcount " << (int)spi_bitcount
[555]57                << " idx " << m_data_idx << " len_snd " << m_datalen_snd
58                << " len_rcv " << m_datalen_rcv << std::endl;
59        }
[556]60        spi_fsm  = S_IDLE;
61        spi_clk = p_spi_clk;
[574]62        spi_mosi_previous = p_spi_mosi;
[552]63        return;
64    }
65
[556]66    switch(spi_fsm) {
[552]67    case S_IDLE:
[556]68        if (p_spi_clk.read() == 1 && spi_clk == 0) {
[552]69                // rising edge
[574]70                command = (command << 1) | spi_mosi_previous;
[556]71                spi_bitcount = 6;
72                spi_fsm = S_RECEIVE_CMD;
[552]73        }
74        break;
75    case S_RECEIVE_CMD:
[556]76        if (p_spi_clk.read() == 1 && spi_clk == 0) {
[552]77                // rising edge
[574]78                command = (command << 1) | spi_mosi_previous;
[556]79                if (spi_bitcount == 0) {
80                        if ((command & 0x80) == 0) {
81                                spi_fsm = S_RECEIVE_ARGS_START;
[552]82                        } else {
[555]83#ifdef SOCLIB_MODULE_DEBUG0
[556]84                                std::cout << name() << " S_RECEIVE_CMD " << std::hex << (int)command << std::endl;
[552]85#endif
[556]86                                spi_fsm = S_IDLE;
[552]87                        }
[556]88                } else {
89                    spi_bitcount = spi_bitcount - 1;
[552]90                }
91        }
92        break;
93    case S_RECEIVE_ARGS_START:
[556]94        if (p_spi_clk.read() == 1 && spi_clk == 0) {
[552]95                // rising edge
[574]96                args = (args << 1) | spi_mosi_previous;
[556]97                spi_bitcount = 30;
98                spi_fsm = S_RECEIVE_ARGS;
[552]99        }
100        break;
101    case S_RECEIVE_ARGS:
[556]102        if (p_spi_clk.read() == 1 && spi_clk == 0) {
[552]103                // rising edge
[574]104                args = (args << 1) | spi_mosi_previous;
[556]105                if (spi_bitcount == 0) {
106                        spi_bitcount = 7;
107                        spi_fsm = S_RECEIVE_CRC;
108                } else {
109                    spi_bitcount = spi_bitcount - 1;
[552]110                }
111        }
112        break;
113    case S_RECEIVE_CRC:
[556]114        if (p_spi_clk.read() == 1 && spi_clk == 0) {
[552]115                // rising edge
[574]116                cmdcrc = (cmdcrc << 1) | spi_mosi_previous;
[556]117                if (spi_bitcount == 0) {
118                        handle_sdmmc_cmd(command, args);
119                        spi_bitcount = 0; // SEND_DATA will reset it
120                        spi_fsm = S_SEND_DATA;
[552]121                        m_data_idx = 0;
122                } else {
[556]123                        spi_bitcount = spi_bitcount - 1;
[552]124                }
125        }
126        break;
127       
128    case S_SEND_DATA:
[556]129        if (p_spi_clk.read() == 0 && spi_clk == 1) {
[552]130                // falling edge
[556]131                if (spi_bitcount == 0) {
[552]132                        if (m_data_idx != m_datalen_snd) {     
[556]133                                spi_shiftreg = m_databuf[m_data_idx];
134                                spi_bitcount = 7;
135                                spi_fsm = S_SEND_DATA;
[552]136                                m_data_idx++;
[555]137#ifdef SOCLIB_MODULE_DEBUG0
[552]138                        std::cout << name() << " S_SEND_DATA " << std::dec << m_datalen_snd << " idx " << m_data_idx << " " << std::hex << (uint32_t)m_databuf[m_data_idx] << std::endl;
139#endif
140                        } else if (m_datalen_rcv != 0) {
[556]141                                spi_fsm = S_RECEIVE_DATA_WAIT;
142                                spi_bitcount = 7;
[552]143                                m_data_idx = 0;
144                        } else {
[556]145                                spi_fsm = S_IDLE;
[552]146                        }
147                } else {
[556]148                        spi_bitcount = spi_bitcount - 1;
149                        spi_shiftreg = spi_shiftreg << 1;
[552]150                }
151        }
152        break;
[555]153    case S_RECEIVE_DATA_WAIT:
[556]154        if (p_spi_clk.read() == 1 && spi_clk == 0) {
155            // rising edge
[555]156            uint8_t s_data;
[574]157            s_data = (m_databuf[0] << 1) | spi_mosi_previous;
[555]158            m_databuf[0] = s_data;
[556]159            if (spi_bitcount == 0) {
[555]160#ifdef SOCLIB_MODULE_DEBUG
161        std::cout << name() << " S_RECEIVE_DATA_WAIT " << std::dec << (int)s_data << std::endl;
162#endif
[556]163                    spi_bitcount = 7;
[555]164                    if (s_data == 0xfe) { // data start token
[556]165                        spi_fsm = S_RECEIVE_DATA;
[555]166                        m_data_idx = 1;
167                    } else {
168#ifdef SOCLIB_MODULE_DEBUG
169                        std::cout << name() << " S_RECEIVE_DATA_WAIT " << std::hex << (int)s_data << std::endl;
170#endif
[556]171                        spi_fsm = S_RECEIVE_DATA_WAIT;
[555]172                }
[556]173            } else {
174                spi_bitcount = spi_bitcount - 1;
[555]175            }
176        }
177        break;
178        case S_RECEIVE_DATA:
[556]179            if (p_spi_clk.read() == 1 && spi_clk == 0) {
[555]180                // rising edge
[574]181                m_databuf[m_data_idx] = (m_databuf[m_data_idx] << 1) | spi_mosi_previous;
[556]182                if (spi_bitcount == 0) {
[555]183                    m_data_idx++;
184                    if (m_data_idx != m_datalen_rcv) {
[556]185                        spi_fsm = S_RECEIVE_DATA;
186                        spi_bitcount = 7;
[555]187                    } else {
[556]188                        handle_sdmmc_write(command, args);
[555]189                        if (m_datalen_snd > 0) {
[556]190                            spi_bitcount = 0; // SEND_DATA will reset it
191                            spi_fsm = S_SEND_DATA;
[555]192                            m_data_idx = 0;
193                        } else {
[556]194                            spi_fsm = S_IDLE;
[555]195                        }
196                    }
197                } else {
[556]198                        spi_bitcount = spi_bitcount - 1;
[555]199                }
200            }
201            break;
[552]202    }
203
[557]204//// now genrate output signal
205
[556]206    switch(spi_fsm) {
[552]207    case S_IDLE:
208        p_spi_miso = !p_spi_ss.read();
209        break;
210    case S_SEND_DATA:
[556]211        p_spi_miso = (spi_shiftreg & 0x80) != 0;
[552]212        break;
213    default:
214        p_spi_miso = !p_spi_ss.read();
215        break;
216    }
[557]217    spi_clk = p_spi_clk.read();
[574]218    spi_mosi_previous = p_spi_mosi;
[557]219} // end GenMealy()
[552]220
221
222//////////////////////
223void SdMMC::handle_sdmmc_cmd(uint8_t cmd, uint32_t data)
224{
225        m_datalen_rcv = 0;
226        m_databuf[0] = 0x04; // illegal command
227        m_datalen_snd = 1;
228
229        if (m_sdstate == SD_IDLE)
230                m_databuf[0] |= 0x01; // idle
231
232        if ((cmd & 0x40) == 0) {
233                //illegal command
234                return;
235        }
236        cmd &= 0x3f;
237        if (m_acmd) {
[555]238#ifdef SOCLIB_MODULE_DEBUG0
[556]239        std::cout << name() << " new acmd " << std::dec << (int)cmd << " args " << std::hex << data << " crc " << (int)cmdcrc << std::endl;
[552]240#endif
241            m_acmd = false;
242            switch (cmd) {
243            case 41:
244                m_databuf[0] = 0x0; // card ready
245                m_datalen_snd = 1;
246                m_sdstate = SD_READY;
247                break;
248            case 51:
249                // send SCR
250                m_databuf[ 0] = (m_sdstate == SD_IDLE) ? 0x1 : 0x0; // R1
251                m_databuf[ 1] = 0xfe; // data token
252                m_databuf[ 2] = 0x00; // SCR_STRUCTURE / SD_SPEC
253                m_databuf[ 3] = 0x05; // DATA_STAT_AFTER_ERASE, SD_SECURITY, SD_BUS_WIDTHS
254                m_databuf[ 4] = 0;    // SD_SPEC3, EX_SECURITY, SD_SPEC4
255                m_databuf[ 5] = 0;    // CMD_SUPPORT
256                m_databuf[ 6] = 0;    // vendor specific
257                m_databuf[ 7] = 0;    // vendor specific
258                m_databuf[ 8] = 0;    // vendor specific
259                m_databuf[ 9] = 0;    // vendor specific
260                m_databuf[10] = 0x0;  // CRC16
261                m_databuf[11] = 0x0;  // CRC16
262                m_datalen_snd = 12;
263                break;
264            default:
265                std::cout << name() << " unknown acmd " << std::dec
266                    << (int)cmd << std::endl;
267                break; // return illegal command
268            }
269        } else {
[555]270#ifdef SOCLIB_MODULE_DEBUG0
[556]271        std::cout << name() << " new cmd " << std::dec << (int)cmd << " args " << std::hex << data << " crc " << (int)cmdcrc << std::endl;
[552]272#endif
273            switch (cmd) {
274            case 0:
275                m_databuf[0] = 0x1;
276                m_datalen_snd = 1;
277                m_sdstate = SD_IDLE;
278                break;
279            case 8:
280                // reply with illegal command for now
281                break;
282            case 9:
283              {
284                // send CSD
285                // we use a block len of 1024
286                uint32_t csize = ((m_device_size + (512 * 1024) - 1) / (512 * 1024)) - 1;
287                m_databuf[ 0]  = (m_sdstate == SD_IDLE) ? 0x1 : 0x0; // R1
288                m_databuf[ 1]  = 0xfe; // data token
289                m_databuf[ 2]  = 0x00; // CSD_STRUCTURE
290                m_databuf[ 3]  = 0xe;  // TAAC
291                m_databuf[ 4]  = 0;    // NSAC
292                m_databuf[ 5]  = 0x32; // TRAN_SPEED
293                m_databuf[ 6]  = 0x5b; // CCC_H
294                m_databuf[ 7]  = 0x5a; // CCC_L + READ_BL_LEN
295                m_databuf[ 8]  = 0x80; // READ_BL_PARTIAL, R/W_BLK_MISALIGN, DSR_IMP
296                m_databuf[ 8] |= (csize >> 10) & 0x03; // CSIZE[12-11]
297                m_databuf[ 9]  = (csize >>  2) & 0xff; // CSIZE[10-2]
298                m_databuf[10]  = (csize <<  6) & 0xc0; // CSIZE[1-0]
299                m_databuf[10] |= 0;    // R_CURR_MIN, R_CURR_MAX
300                m_databuf[11]  = 0x3;  // W_CURR_MIN, W_CURR_MAX, CSIZE_MULT[2-1];
301                m_databuf[12]  = 0xff; // CSIZE_MULT[1], ERASE_BLK_EN, ERASE_SECTOR_SIZE[6-1]
302                m_databuf[13]  = 0x80; // ERASE_SECTOR_SIZE[0]. WP_GRP_SIZE
303                m_databuf[14]  = 0x0a; // WP_GRP_ENABLE, R2W_FACTOR, WRITE_BL_LEN[2-3]
304                m_databuf[15]  = 0x40; // WRITE_BL_LEN[0-1], WR_BL_PARTIAL
305                m_databuf[16]  = 0;    // FILE_FORMAT
306                m_databuf[17]  = 0x1;  // CRC7
307                m_databuf[18]  = 0x0;  // CRC16
308                m_databuf[19]  = 0x0;  // CRC16
309                m_datalen_snd  = 20;
310                break;
311              }
312            case 10:
313                // send CID
314                m_databuf[ 0] = (m_sdstate == SD_IDLE) ? 0x1 : 0x0; // R1
315                m_databuf[ 1] = 0xfe; // data token
316                m_databuf[ 2] = 0xda; // MID
317                m_databuf[ 3] = 'P';  // OID
318                m_databuf[ 4] = '6';  // OID
319                m_databuf[ 5] = 's';  // PNM
320                m_databuf[ 6] = 'o';  // PNM
321                m_databuf[ 7] = 'c';  // PNM
322                m_databuf[ 8] = 's';  // PNM
323                m_databuf[ 9] = 'd';  // PNM
324                m_databuf[10] = 0x01; // PRV
325                m_databuf[11] = 0xde; // PSN
326                m_databuf[12] = 0xad; // PSN
327                m_databuf[13] = 0xbe; // PSN
328                m_databuf[14] = 0xef; // PSN
329                m_databuf[15] = 10;   // MDT
330                m_databuf[16] = 13;   // MDT
331                m_databuf[17] = 0x1;  // CRC7
332                m_databuf[18] = 0x0;  // CRC16
333                m_databuf[19] = 0x0;  // CRC16
334                m_datalen_snd = 20;
335                break;
336            case 16:
337                // set block size
338                if (m_sdstate != SD_IDLE && data == 512) {
339                        m_databuf[0] = 0x00;
340                        m_datalen_snd = 1;
341                } // else illegal command
342                break;
343            case 17:
344              {
345                int ret;
346                // read data block
347                if (m_sdstate == SD_IDLE) {
348                        // return illegal command
349                        return;
350                }
351                if (data >= m_device_size) {
352                        std::cerr << name() << " read: request " << data
353                            << " past end of file " << m_device_size << std::endl;
354                        m_databuf[0] = 0x00; // R1 OK
355                        m_databuf[1] = 0x08; // error tocken "out of range"
356                        m_datalen_snd = 2;
357                        return;
358                }
359                do {
360                        if (lseek(m_fd, data, SEEK_SET) < 0) {
361                                std::cerr << name() << " lseek: " <<
362                                  strerror(errno) << std::endl;
363                                m_databuf[0] = 0x00; // R1 OK
364                                m_databuf[1] = 0x02; // error tocken "CC err"
365                                m_datalen_snd = 2;
366                                return;
367                        }
368                        ret = read(m_fd, &m_databuf[2], 512);
369                } while (ret < 0 && errno == EINTR);
370                if (ret < 0) {
371                        std::cerr << name() << " read: " <<
372                          strerror(errno) << std::endl;
373                        m_databuf[0] = 0x00; // R1 OK
374                        m_databuf[1] = 0x04; // error tocken "card ECC failed"
375                        m_datalen_snd = 2;
376                        return;
377                }
378                m_databuf[514] = m_databuf[515] = 0; // XXX CRC
379                m_databuf[0] = 0x0; // R1
380                m_databuf[1] = 0xfe; // start block tocken
381                m_datalen_snd = 516;
382                break;
383              }
[555]384            case 24:
385              {
386                // write data block
387                if (m_sdstate == SD_IDLE) {
388                        // return illegal command
389                        return;
390                }
391#ifdef SOCLIB_MODULE_DEBUG
392        std::cout << name() << " new cmd write " << std::dec << (int)cmd << " args " << std::hex << data << std::endl;
393#endif
394                m_databuf[0] = 0x0; // R1
395                m_datalen_snd = 1;
396                m_datalen_rcv = 512 + 2 + 1; // data + tocken + CRC
397                break;
398              }
[552]399            case 55:
400                // app-specific command follow
401                m_acmd = true;
402                m_databuf[0] = (m_sdstate == SD_IDLE) ? 0x1 : 0x0;
403                m_datalen_snd = 1;
404                break;
405            case 58:
406                // send OCR
407                m_databuf[4] = (m_sdstate == SD_IDLE) ? 0x1 : 0x0; // R1
408                m_databuf[3] = 0x80; // power up complete, SDSC
409                m_databuf[2] = 0xff; // all voltages supported
410                m_databuf[1] = 0x00; 
411                m_databuf[0] = 0x00; 
412                m_datalen_snd = 5;
413                break;
414            default:
415                std::cout << name() << " unknown cmd " << std::dec
416                    << (int)cmd << std::endl;
417                break; // return illegal command
418            }
419        }
420}
421
[555]422void SdMMC::handle_sdmmc_write(uint8_t cmd, uint32_t data)
[552]423{
[555]424        m_datalen_rcv = 0;
425        cmd &= 0x3f;
426#ifdef SOCLIB_MODULE_DEBUG
427        std::cout << name() << " cmd write " << std::dec << (int)cmd << " args " << std::hex << data << std::endl;
428#endif
429        switch(cmd) {
430            case 24:
431              {
432                int ret;
433                // write data block
434                assert(m_sdstate != SD_IDLE && "can't write in idle state");
435                if (data >= m_device_size) {
436                        std::cerr << name() << " write: request " << data
437                            << " past end of file " << m_device_size << std::endl;
438                        m_databuf[0] = 0xd; // write error
439                        m_datalen_snd = 1;
440                        return;
441                }
442                do {
443                        if (lseek(m_fd, data, SEEK_SET) < 0) {
444                                std::cerr << name() << " lseek: " <<
445                                  strerror(errno) << std::endl;
446                                m_databuf[0] = 0xd; // write error
447                                m_datalen_snd = 1;
448                                return;
449                        }
450                        ret = write(m_fd, &m_databuf[1], 512);
451                } while (ret < 0 && errno == EINTR);
452                if (ret < 0) {
453                        std::cerr << name() << " write: " <<
454                          strerror(errno) << std::endl;
455                        m_databuf[0] = 0xd; // write error
456                        m_datalen_snd = 1;
457                        return;
458                }
459                m_databuf[0] = 0x5; // write complete
460                m_databuf[1] = 0x0; // busy
461                m_datalen_snd = 2;
462                break;
463              }
464            default:
465                std::cerr << name() << " unkown write cmd " << std::dec <<
466                    (int)cmd << std::endl;
467                m_databuf[0] = 0xd; // write error;
468                m_datalen_snd = 1;
469        }
[552]470        return;
471}
472
473//////////////////////////////////////////////////////////////////////////////
474SdMMC::SdMMC( sc_core::sc_module_name              name, 
475                                const std::string                    &filename,
476                                const uint32_t                       latency)
477
478: caba::BaseModule(name),
479        m_latency(latency),
480        p_clk("p_clk"),
481        p_resetn("p_resetn"),
482        p_spi_ss("p_spi_ss"),
483        p_spi_clk("p_spi_clk"),
484        p_spi_mosi("p_spi_mosi"),
485        p_spi_miso("p_spi_miso")
486{
487    std::cout << "  - Building SdMMC " << name << std::endl;
488
[557]489    SC_METHOD(genMealy);
[552]490    dont_initialize();
491    sensitive << p_clk.neg();
[557]492    sensitive << p_resetn;
493    sensitive << p_spi_ss;
494    sensitive << p_spi_clk;
[552]495
496    m_fd = ::open(filename.c_str(), O_RDWR);
497    if ( m_fd < 0 ) 
498    {
499            std::cout << "Error in component SdMMC : " << name
500                      << " Unable to open file " << filename << std::endl;
501            exit(1);
502    }
503    m_device_size = lseek(m_fd, 0, SEEK_END);
504
505} // end constructor
506
507SdMMC::~SdMMC()
508{
509}
510
511
512//////////////////////////
513void SdMMC::print_trace()
514{
515        const char* spi_str[] = 
516    {
517                "S_IDLE",
518                "S_RECEIVE_CMD",
519                "S_RECEIVE_ARGS_START",
520                "S_RECEIVE_ARGS",
521                "S_RECEIVE_CRC",
[555]522                "S_RECEIVE_DATA_START",
[552]523                "S_RECEIVE_DATA",
524                "S_SEND_DATA",
525                "S_NOP",
526        };
[556]527        if (spi_clk != p_spi_clk.read()) {
528        std::cout << name() << " SPI_FSM : " << spi_str[spi_fsm] 
[552]529            << std::dec
[556]530            << " clk " << spi_clk << "->" << p_spi_clk << " ss " << p_spi_ss
[552]531            << " mosi " << p_spi_mosi << " miso " << p_spi_miso
532            << std::endl;
[556]533        std::cout << "         spi_shiftreg: " << std::hex << (int)spi_shiftreg
534            << " spi_bitcount: " << (int)spi_bitcount
[552]535            << std::endl;
536        }
537}
538}} // end namespace
539
540// Local Variables:
541// tab-width: 4
542// c-basic-offset: 4
543// c-file-offsets:((innamespace . 0)(inline-open . 0))
544// indent-tabs-mode: nil
545// End:
546
547// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
548
Note: See TracBrowser for help on using the repository browser.