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

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

Add a SPI SD/MMC card model. Write command not implemented yet.
Known to work with the TSAR SPI boot loader and NetBSD's sdmmc driver.
The data are backed by a file in the host's filesystem, as vci_block_device
does.
The purpose of this model is to provide a sdmmc device to the vci_spi
controller. It's not intended to respect timings or a real SD/MMC device.

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