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

Last change on this file was 1052, checked in by alain, 7 years ago

few bugs

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