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

Last change on this file since 553 was 552, checked in by bouyer, 13 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.