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

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

Implement single block write

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