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

Last change on this file since 556 was 556, checked in by bouyer, 11 years ago

Convert all sc_signal to plain integer types. Tracking edges of the spi_clock
from the main clock can lead to a 180deg phase shift otherwise when
spi_clock is just the main clock divided by 2.

File size: 14.6 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 <iostream>
33#include <fcntl.h>
34#include <cassert>
35#include "sdmmc.h"
36
37namespace soclib { namespace caba {
38
39using namespace soclib::caba;
40
41////////////////////////
42void SdMMC::transition()
43{
44    if(p_resetn.read() == false) 
45    {
46        spi_fsm  = S_IDLE;
47        m_acmd     = false;     
48        m_sdstate  = SD_IDLE;
49        return;
50    } 
51    if (p_spi_ss.read()) {
52        if (spi_fsm != S_IDLE) {
53                std::cerr << name() << " deselect but not idle, state "
54                << std::dec << spi_fsm << " last cmd " << (int)command
55                << " args " << std::hex << args << std::dec
56                << " bitcount " << (int)spi_bitcount
57                << " idx " << m_data_idx << " len_snd " << m_datalen_snd
58                << " len_rcv " << m_datalen_rcv << std::endl;
59        }
60        spi_fsm  = S_IDLE;
61        spi_clk = p_spi_clk;
62        return;
63    }
64
65    switch(spi_fsm) {
66    case S_IDLE:
67        if (p_spi_clk.read() == 1 && spi_clk == 0) {
68                // rising edge
69                command = (command << 1) | p_spi_mosi;
70                spi_bitcount = 6;
71                spi_fsm = S_RECEIVE_CMD;
72        }
73        break;
74    case S_RECEIVE_CMD:
75        if (p_spi_clk.read() == 1 && spi_clk == 0) {
76                // rising edge
77                command = (command << 1) | p_spi_mosi;
78                if (spi_bitcount == 0) {
79                        if ((command & 0x80) == 0) {
80                                spi_fsm = S_RECEIVE_ARGS_START;
81                        } else {
82#ifdef SOCLIB_MODULE_DEBUG0
83                                std::cout << name() << " S_RECEIVE_CMD " << std::hex << (int)command << std::endl;
84#endif
85                                spi_fsm = S_IDLE;
86                        }
87                } else {
88                    spi_bitcount = spi_bitcount - 1;
89                }
90        }
91        break;
92    case S_RECEIVE_ARGS_START:
93        if (p_spi_clk.read() == 1 && spi_clk == 0) {
94                // rising edge
95                args = (args << 1) | p_spi_mosi;
96                spi_bitcount = 30;
97                spi_fsm = S_RECEIVE_ARGS;
98        }
99        break;
100    case S_RECEIVE_ARGS:
101        if (p_spi_clk.read() == 1 && spi_clk == 0) {
102                // rising edge
103                args = (args << 1) | p_spi_mosi;
104                if (spi_bitcount == 0) {
105                        spi_bitcount = 7;
106                        spi_fsm = S_RECEIVE_CRC;
107                } else {
108                    spi_bitcount = spi_bitcount - 1;
109                }
110        }
111        break;
112    case S_RECEIVE_CRC:
113        if (p_spi_clk.read() == 1 && spi_clk == 0) {
114                // rising edge
115                cmdcrc = (cmdcrc << 1) | p_spi_mosi;
116                if (spi_bitcount == 0) {
117                        handle_sdmmc_cmd(command, args);
118                        spi_bitcount = 0; // SEND_DATA will reset it
119                        spi_fsm = S_SEND_DATA;
120                        m_data_idx = 0;
121                } else {
122                        spi_bitcount = spi_bitcount - 1;
123                }
124        }
125        break;
126       
127    case S_SEND_DATA:
128        if (p_spi_clk.read() == 0 && spi_clk == 1) {
129                // falling edge
130                if (spi_bitcount == 0) {
131                        if (m_data_idx != m_datalen_snd) {     
132                                spi_shiftreg = m_databuf[m_data_idx];
133                                spi_bitcount = 7;
134                                spi_fsm = S_SEND_DATA;
135                                m_data_idx++;
136#ifdef SOCLIB_MODULE_DEBUG0
137                        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;
138#endif
139                        } else if (m_datalen_rcv != 0) {
140                                spi_fsm = S_RECEIVE_DATA_WAIT;
141                                spi_bitcount = 7;
142                                m_data_idx = 0;
143                        } else {
144                                spi_fsm = S_IDLE;
145                        }
146                } else {
147                        spi_bitcount = spi_bitcount - 1;
148                        spi_shiftreg = spi_shiftreg << 1;
149                }
150        }
151        break;
152    case S_RECEIVE_DATA_WAIT:
153        if (p_spi_clk.read() == 1 && spi_clk == 0) {
154            // rising edge
155            uint8_t s_data;
156            s_data = (m_databuf[0] << 1) | p_spi_mosi;
157            m_databuf[0] = s_data;
158            if (spi_bitcount == 0) {
159#ifdef SOCLIB_MODULE_DEBUG
160        std::cout << name() << " S_RECEIVE_DATA_WAIT " << std::dec << (int)s_data << std::endl;
161#endif
162                    spi_bitcount = 7;
163                    if (s_data == 0xfe) { // data start token
164                        spi_fsm = S_RECEIVE_DATA;
165                        m_data_idx = 1;
166                    } else {
167#ifdef SOCLIB_MODULE_DEBUG
168                        std::cout << name() << " S_RECEIVE_DATA_WAIT " << std::hex << (int)s_data << std::endl;
169#endif
170                        spi_fsm = S_RECEIVE_DATA_WAIT;
171                }
172            } else {
173                spi_bitcount = spi_bitcount - 1;
174            }
175        }
176        break;
177        case S_RECEIVE_DATA:
178            if (p_spi_clk.read() == 1 && spi_clk == 0) {
179                // rising edge
180                m_databuf[m_data_idx] = (m_databuf[m_data_idx] << 1) | p_spi_mosi;
181                if (spi_bitcount == 0) {
182                    m_data_idx++;
183                    if (m_data_idx != m_datalen_rcv) {
184                        spi_fsm = S_RECEIVE_DATA;
185                        spi_bitcount = 7;
186                    } else {
187                        handle_sdmmc_write(command, args);
188                        if (m_datalen_snd > 0) {
189                            spi_bitcount = 0; // SEND_DATA will reset it
190                            spi_fsm = S_SEND_DATA;
191                            m_data_idx = 0;
192                        } else {
193                            spi_fsm = S_IDLE;
194                        }
195                    }
196                } else {
197                        spi_bitcount = spi_bitcount - 1;
198                }
199            }
200            break;
201    }
202    spi_clk = p_spi_clk.read();
203}  // end transition
204
205//////////////////////
206void SdMMC::genMoore()
207{
208    switch(spi_fsm) {
209    case S_IDLE:
210        p_spi_miso = !p_spi_ss.read();
211        break;
212    case S_SEND_DATA:
213        p_spi_miso = (spi_shiftreg & 0x80) != 0;
214        break;
215    default:
216        p_spi_miso = !p_spi_ss.read();
217        break;
218    }
219} // end GenMoore()
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) {
238#ifdef SOCLIB_MODULE_DEBUG0
239        std::cout << name() << " new acmd " << std::dec << (int)cmd << " args " << std::hex << data << " crc " << (int)cmdcrc << std::endl;
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 {
270#ifdef SOCLIB_MODULE_DEBUG0
271        std::cout << name() << " new cmd " << std::dec << (int)cmd << " args " << std::hex << data << " crc " << (int)cmdcrc << std::endl;
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              }
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              }
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
422void SdMMC::handle_sdmmc_write(uint8_t cmd, uint32_t data)
423{
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        }
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
489        SC_METHOD(transition);
490    dont_initialize();
491    sensitive << p_clk.pos();
492
493        SC_METHOD(genMoore);
494    dont_initialize();
495    sensitive << p_clk.neg();
496
497    m_fd = ::open(filename.c_str(), O_RDWR);
498    if ( m_fd < 0 ) 
499    {
500            std::cout << "Error in component SdMMC : " << name
501                      << " Unable to open file " << filename << std::endl;
502            exit(1);
503    }
504    m_device_size = lseek(m_fd, 0, SEEK_END);
505
506} // end constructor
507
508SdMMC::~SdMMC()
509{
510}
511
512
513//////////////////////////
514void SdMMC::print_trace()
515{
516        const char* spi_str[] = 
517    {
518                "S_IDLE",
519                "S_RECEIVE_CMD",
520                "S_RECEIVE_ARGS_START",
521                "S_RECEIVE_ARGS",
522                "S_RECEIVE_CRC",
523                "S_RECEIVE_DATA_START",
524                "S_RECEIVE_DATA",
525                "S_SEND_DATA",
526                "S_NOP",
527        };
528        if (spi_clk != p_spi_clk.read()) {
529        std::cout << name() << " SPI_FSM : " << spi_str[spi_fsm] 
530            << std::dec
531            << " clk " << spi_clk << "->" << p_spi_clk << " ss " << p_spi_ss
532            << " mosi " << p_spi_mosi << " miso " << p_spi_miso
533            << std::endl;
534        std::cout << "         spi_shiftreg: " << std::hex << (int)spi_shiftreg
535            << " spi_bitcount: " << (int)spi_bitcount
536            << std::endl;
537        }
538}
539}} // end namespace
540
541// Local Variables:
542// tab-width: 4
543// c-basic-offset: 4
544// c-file-offsets:((innamespace . 0)(inline-open . 0))
545// indent-tabs-mode: nil
546// End:
547
548// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
549
Note: See TracBrowser for help on using the repository browser.