A lot of interface documentation.
authorMalte S. Stretz <mss@apache.org>
Sun, 5 Jul 2009 16:35:03 +0000 (18:35 +0200)
committerMalte S. Stretz <mss@apache.org>
Sun, 5 Jul 2009 16:35:03 +0000 (18:35 +0200)
bos2k9.vhd
bos2k9_globals.vhd
bos2k9_mmu.vhd
bos2k9_t.vhd
fhw_sd/sd_commands_p.vhd
fhw_sd/sd_counter_e.vhd
fhw_sd/sd_flow_e.vhd
fhw_sd/sd_globals_p.vhd
fhw_sd/sd_host.vhd
fhw_sd/sd_parser_e.vhd

index 8e1fee2..7216880 100644 (file)
@@ -1,3 +1,62 @@
+-----------------------------------------------------------------------
+-- Copyright (c) 2009 Malte S. Stretz <http://msquadrat.de> 
+--
+-- The project top level entity.
+--
+-- It implements a simple test setup which can read data from an SD 
+-- card.  Blocks are read in 512 Byte blocks, so both block addresses
+-- and byte addresses (relative to the block start) can be specified.
+-- The system starts up doing nothing, an init button has to be pressed
+-- to initialize the card and afterwards the selected block can be read
+-- to an internal buffer.
+--
+-- This is designed around the DE2 evaluation board.  To simplify
+-- development, the ports of the entity are named after the file
+-- `DE2_Pin_Table.pdf`, which is part of the DE2 documentation.
+-- The PDF file was converted to a TCL file and is included in this
+-- project as `de2_pins.tcl` and can be copied to the `bos2k9.qsf`
+-- project file.  This has the side effect that Quartus will complain
+-- that some of these pins are stuck to GND or not used; these 
+-- warnings can be ignored.
+--
+-- The following pins are used:
+--  * `CLOCK_50` is the 50 MHz system clock.
+--  * `KEY` are the four push button which are low-active.
+--  * `SW` are the eighteen on-off switches.
+--  * `LEDR` are the eighteen red LEDs above the switches.
+--  * `LEDG` are the nine green LEDs; the low eight are located above
+--    the push buttons, the ninth is above the row of red LEDs.
+--  * `SD_DAT` is the SPI MISO.
+--  * `SD_CMD` is the SPI MOSI.
+--  * `SD_DAT3` is the SPI CS.
+--  * `SD_CLK` is the SPI SCK.
+--
+-- LEDG(0) should be always on and represents a powered system. The 
+-- `reset` is wired to `SW(17)`, so the switch should be off when these
+-- system is started.  Once `reset` is off (ie. the switch on), the card
+-- can be initialized (and later reset) by pressing `KEY(0)`.  Once
+-- LEDG(2) is led, the system is ready to read a block; if an error 
+-- occurs, LEDG(1) is switched on instead.
+--
+-- The low eight bits of the block address can be specified by the
+-- first eight `SW`es, ie. SW(0) to SW(7).  Only the first 256 blocks 
+-- of 4096 possible ones (on 2 GiB SD cards; SDHC is not supported) can 
+-- be read.  `KEY(1)` starts the reading of the selected block.
+--
+-- The used `button` entity ensures that even with really slow fingers,
+-- the button press is only signaled once.  As the buttons on the DE2
+-- board tend to break, this cannot be ensured but a longer press 
+-- doesn't break anything.
+--
+-- The currently via SW(8) to SW(15) selected byte is displayed on the
+-- LEDs LEDR(0) to LEDR(7).  Because only eight bit are wired, only 
+-- half the block can be displayed.  Both this and the above limitation 
+-- doesn't matter as this is a test setup only.
+--
+-- For debugging purposes, the SPI bus is also wired to the LEDs 
+-- LEDG(7) to LEDG(4). 
+-----------------------------------------------------------------------
+
 library ieee;
 use ieee.std_logic_1164.all;
 
@@ -16,16 +75,14 @@ entity bos2k9 is
   port(
     CLOCK_50 : in std_logic;
     
-    --GPIO_0 : inout std_logic_vector(35 downto 0);
-    
     KEY  : in  std_logic_vector(3 downto 0);
     SW   : in  std_logic_vector(17 downto 0);
     LEDR : out std_logic_vector(17 downto 0);
     LEDG : out std_logic_vector(8 downto 0);
     
     SD_DAT  : in  std_logic;
-    SD_DAT3 : out std_logic;
     SD_CMD  : out std_logic;
+    SD_DAT3 : out std_logic;
     SD_CLK  : out std_logic);
 end bos2k9;
 
@@ -95,11 +152,8 @@ architecture board of bos2k9 is
   
 begin
   clock_s <= CLOCK_50;
-  --reset_s <= GPIO_0(15);
   reset_s <= not SW(17);
 
-  --GPIO_0 <= (others => 'Z');
-  
   init_button : button port map(clock_s, reset_s,
     input  => KEY(0),
     output => init_btn_s);
index 2ae5429..66591f1 100644 (file)
@@ -1,3 +1,9 @@
+-----------------------------------------------------------------------
+-- Copyright (c) 2009 Malte S. Stretz <http://msquadrat.de> 
+--
+-- Project settings, constants and global helper types.
+--
+-----------------------------------------------------------------------
 library ieee;
 use ieee.std_logic_1164.all;
 
@@ -6,14 +12,24 @@ use fhw_sd.sd_globals_p.all;
 
 package bos2k9_globals is
 
+  -- The DE2 board runs with 50 MHz.
   constant clock_interval_c : time   := 20 ns;
-  constant sd_clock_div_c : positive := 128; -- 390.625 kHz (max 400 kHz); exakt: 125
+  -- The maximum initial rate for the SD card is 400 kHz, which would
+  -- result in a clock divider of 125.  To be on the safe side, we'll
+  -- run at 390.625 kHz.
+  constant sd_clock_div_c : positive := 128; 
   
+  -- Pull in this type from fhw_sd.sd_globals_p; it is used to address
+  -- a 512 B block on the SD card.
   subtype  std_logic_block_address_t is std_logic_block_address_t;
+  -- The first block.
   constant zero_block_address_c : std_logic_block_address_t := (others => '0');
   
-  constant byte_address_width_c : positive := 9; -- 512 bytes per block
+  -- There are 512 B per block, so we need a 9-bit address to access
+  -- the bytes.
+  constant byte_address_width_c : positive := 9;
   subtype  std_logic_byte_address_t is std_logic_vector(byte_address_width_c - 1 downto 0);
+  -- The first byte.
   constant zero_byte_address_c : std_logic_byte_address_t := (others => '0');
 
 end bos2k9_globals;
index b74b90e..fe32a89 100644 (file)
@@ -1,3 +1,20 @@
+-----------------------------------------------------------------------
+-- Copyright (c) 2009 Malte S. Stretz <http://msquadrat.de> 
+--
+-- A simple shift register with separate read and write addresses
+-- around a single 512 Byte M5K block implemented in the
+-- MegaWizard generated entity `mf_block_ram'.
+--
+-- Input data applied to `write_data` is latched in by a high signal
+-- `write_next`.  The current `write_addr` is increased by one.
+--
+-- Output data at the current `read_addr` can be read via `read_data`.
+--
+-- Writing to and reading from the same address will result in the old 
+-- data.
+--
+-----------------------------------------------------------------------
+
 library ieee;
 use ieee.std_logic_1164.all;
 use ieee.numeric_std.all;
@@ -26,11 +43,9 @@ architecture rtl of bos2k9_mmu is
   component mf_block_ram is
     port(
       clock : in  std_logic;
-
       wraddress : in  std_logic_byte_address_t;
       wren      : in  std_logic;
       data      : in  std_logic_byte_t;
-      
       rdaddress : in  std_logic_byte_address_t;
       q         : out std_logic_byte_t);
   end component;
index 149ee38..6ece02f 100644 (file)
@@ -2,6 +2,23 @@
 -- Copyright (c) 2009 Malte S. Stretz <http://msquadrat.de> 
 --
 -- Testing the top level entity.
+--
+-- SPI input data is read from the text file `bos2k9_t.dat` (or the
+-- filename specified by the generic `spi_filename`). Each line 
+-- consists of two bytes, std_logic style and separated by a single 
+-- space: The first byte is the data expected from the SPI master (ie. 
+-- the SD host), the second the reply to be sent. All following 
+-- characters on the line are ignored and can be used as a comment.
+--
+-- Of course lines are only read when data is read, not while the 
+-- system is idle.
+--
+-- If the data sent by the SD host doesn't equal the expected data,
+-- an assertion is raised. Each input and output data is printed on 
+-- stdout.
+--
+-- This test should be run about 1500 us to reach the first simulated
+-- read.  A full 512 Byte block needs about 15 ms.
 -----------------------------------------------------------------------
 
 use work.bos2k9_globals.all;
@@ -38,11 +55,14 @@ architecture test of bos2k9_t is
       LEDG : out std_logic_vector(8 downto 0);
     
       SD_DAT  : in  std_logic;
-      SD_DAT3 : out std_logic;
       SD_CMD  : out std_logic;
+      SD_DAT3 : out std_logic;
       SD_CLK  : out std_logic);
   end component;
   
+  -- These are the low eight bytes sent as the read address;
+  -- if this constant is changed, the data file has to be
+  -- modified as well.
   constant addr_sw_c : std_logic_byte_t := "01101010";
 
   file   spi_file : text open read_mode is spi_filename;
@@ -56,8 +76,8 @@ architecture test of bos2k9_t is
   signal LEDR_o_s     : std_logic_vector(17 downto 0);
   signal LEDG_o_s     : std_logic_vector(8 downto 0);
   signal SD_DAT_i_s   : std_logic;
-  signal SD_DAT3_o_s  : std_logic;
   signal SD_CMD_o_s   : std_logic;
+  signal SD_DAT3_o_s  : std_logic;
   signal SD_CLK_o_s   : std_logic;
   
   signal init_s  : std_logic;
@@ -79,14 +99,16 @@ begin
     LEDR_o_s,
     LEDG_o_s,
     SD_DAT_i_s,
-    SD_DAT3_o_s,
     SD_CMD_o_s,
+    SD_DAT3_o_s,
     SD_CLK_o_s);
   SD_DAT_i_s <= spi_s.miso;
   spi_s.mosi <= SD_CMD_o_s;
   spi_s.sck  <= SD_CLK_o_s;
   spi_s.cs   <= SD_DAT3_o_s;
   
+  -- Make sure to change these lines if the board pin assignments 
+  -- are modified.
   byte_dw_s           <= LEDR_o_s(7 downto 0);
   SW_i_s(7 downto 0)  <= addr_sw_s;
   SW_i_s(15 downto 8) <= byte_sw_s;
@@ -95,13 +117,12 @@ begin
   KEY_i_s(0)          <= not init_btn_s;
   KEY_i_s(1)          <= not start_s;
   KEY_i_s(3 downto 2) <= (others => '1');
-  
   error_s <= LEDG_o_s(0);
   ready_s <= LEDG_o_s(1);
-  
   addr_sw_s <= addr_sw_c;
   byte_sw_s <= (others => '0');
   
+  -- Send the init and the start signal.
   stimulus : process
   begin
     init_s  <= '0';
@@ -120,6 +141,8 @@ begin
     wait;
   end process;
   
+  -- Validate input and output against the data in the data file.
+  -- Uses helper routines from txt_util.
   slave : process
     procedure read_skip_header is
       variable line_v  : line;
@@ -169,6 +192,7 @@ begin
     end loop;
   end process;
   
+  -- Simulate slow fingers on the init button.
   button: process
   begin
     init_btn_s <= '0';
@@ -179,19 +203,6 @@ begin
     wait until rising_edge(clock_s);
   end process;
   
-  -- mark: process
-  -- begin
-    -- test_s <= -1;
-    -- wait until falling_edge(reset_s);
-    -- test_s <= test_s + 1;
-    -- while true loop
-      -- if (init_s or start_s) = '1' then
-        -- test_s <= test_s + 1;
-      -- end if;
-      -- wait until rising_edge(clock_s);
-    -- end loop;
-  -- end process;
-  
   reset : process
   begin
     reset_s <= '1';
index fbe9298..caed1be 100644 (file)
@@ -1,8 +1,16 @@
 -----------------------------------------------------------------------
 -- Copyright (c) 2009 Malte S. Stretz <http://msquadrat.de> 
 --
--- Some SD related types.
-
+-- This package contains constants for all the supported commands used
+-- by the entites `sd_parser` and `sd_flow`.
+--
+-- Constants starting with `cmd_do_` are internal commands, real SD
+-- commands use only the prefix `cmd_` followed by the official 
+-- mnemonic name.
+--
+-- A lot of the arguments (especially the internal ones) are constant
+-- and specified here as well.
+-- 
 -----------------------------------------------------------------------
 -- This entity is part of the following library:
 -- pragma library fhw_sd
@@ -13,27 +21,37 @@ library ieee;
 use ieee.std_logic_1164.all;
 
 package sd_commands_p is
+  -- CMD0: GO_IDLE_STATE: all-zero argument
   constant cmd_go_idle_state_c : std_logic_cmd_t := to_cmd(cmd_type_std_c, 0);
   constant arg_go_idle_state_c : std_logic_arg_t := arg_null_c;
   
+  -- CMD1: SEND_OP_COND: all-zero argument
   constant cmd_send_op_cond_c : std_logic_cmd_t := to_cmd(cmd_type_std_c, 1);
   constant arg_send_op_cond_c : std_logic_arg_t := arg_null_c;
   
+  -- CMD16: SET_BLOCKLEN: enforce 512 Byte blocklen
   constant cmd_set_blocklen_c : std_logic_cmd_t := to_cmd(cmd_type_std_c, 16);
   constant arg_set_blocklen_c : std_logic_arg_t := to_arg(512);
   
+  -- CMD17: READ_SINGLE_BLOCK: address has to be padded/shifted by nine bit
   constant cmd_read_single_block_c : std_logic_cmd_t := to_cmd(cmd_type_std_c, 17);
   constant pad_read_single_block_c : std_logic_vector(31 - block_address_width_c downto 0) := (others => '0');
   
+  -- Internal: START: apply at least 75 SCKs; 10 Byte are 80 SCK
   constant cmd_do_start_c : std_logic_cmd_t := to_cmd(cmd_type_int_c, 1);
-  constant arg_do_start_c : std_logic_arg_t := to_arg(10); -- 75+ SCKs (10 byte)
+  constant arg_do_start_c : std_logic_arg_t := to_arg(10);
   
+  -- Internal: SEEK: try to detect Data Token.  The argument is the timeout
+  -- which isn't defined anywhere so we use a big value.
   constant cmd_do_seek_c : std_logic_cmd_t := to_cmd(cmd_type_int_c, 2);
-  constant arg_do_seek_c : std_logic_arg_t := to_arg(50); -- TODO: How many SCKs timeout?
+  constant arg_do_seek_c : std_logic_arg_t := to_arg(50);
   
+  -- Internal: PIPE: directly pipe incoming data from the SPI master to the
+  -- output.  Do this 512 times.
   constant cmd_do_pipe_c : std_logic_cmd_t := to_cmd(cmd_type_int_c, 3);
   constant arg_do_pipe_c : std_logic_arg_t := to_arg(512);
   
+  -- Internal: SKIP: ignore the 16 bit CRC following the data.
   constant cmd_do_skip_c : std_logic_cmd_t := to_cmd(cmd_type_int_c, 4);
-  constant arg_do_skip_c : std_logic_arg_t := to_arg(2); -- CRC16
+  constant arg_do_skip_c : std_logic_arg_t := to_arg(2);
 end sd_commands_p;
index 3dd229f..7de70c9 100644 (file)
@@ -1,7 +1,16 @@
 -----------------------------------------------------------------------
 -- Copyright (c) 2009 Malte S. Stretz <http://msquadrat.de> 
 --
--- TODO
+-- This is a helper entity implementing a counter. It is driven by
+-- `clock` and and can only be reset by applying a high signal to
+-- `rewind` for at least one `clock`.
+--
+-- 
+-- While `enable` is high, it counts the number of ticks given via 
+-- `top`; the maximim is specified by the generic `max`. When that
+-- value is reached, `done` is high for exactly one
+-- `clock` (even if `enable` goes low).
+-- 
 -----------------------------------------------------------------------
 -- This entity is part of the following library:
 -- pragma library fhw_sd
index 0224138..444ce06 100644 (file)
@@ -1,8 +1,81 @@
 -----------------------------------------------------------------------
 -- Copyright (c) 2009 Malte S. Stretz <http://msquadrat.de> 
 --
--- TODO: description
-
+-- Command flow state machine.
+-- 
+-- This helper entity handles the SD/SPI protocol flow in combination
+-- with the `sd_parser_e`.
+--
+-- The following ports are connected directly to the `sd_host` ports:
+--  * `clock`: input, from `clk`
+--  * `reset`: input, from `rst`
+--  * `init`: output
+--  * `ready`: output
+--  * `address`: input
+--  * `start`: input
+--  * `error`: input
+--  * `resetted`: output, to `cs`
+--
+-- The following ports are connected to the `sd_parser_e` ports:
+--  * `command`: output, SD command
+--  * `argument`: output, SD command argument
+--  * `trigger`: output, start `sd_parser_e`
+--  * `shifting`: input, `sd_parser_e` busy
+--  * `error`: input, SD error occurred
+--  * `idled`: input, SD idle mode
+-- 
+-- The protocol flow is handled by one big state machine handled
+-- by the signals `curr_state_s`, `next_state_s`, and `prev_state_s.
+--
+-- There are four different kind of states, some of them branching
+-- via `next_state_s`:
+--
+-- The idle states `rset_state_c` and `wait_state_c` loop until the
+-- external events `start` or `init` happen.  In `rset_state_c` the
+-- SD card is resetted by pulling the CS via `resetted` high.  An
+-- `init` starts the initialization procedure which ends in
+-- `wait_state_c` if successful.  Here, a `start` kicks off the block
+-- reading and another `init` throws the system back into 
+-- `rset_state_c`.
+-- 
+-- The protocol states are most states followed by `send_state_c` and
+-- the intermediate state `loop_state_c`.  These set the `command` and
+-- `argument` to be sent to the `sd_parser_e`.  There are both real SD
+-- commands and internal ones (cf. description of `sd_parser_e`).  All
+-- commands are defined in the package `sd_commands_p`.  The protocol
+-- flow is determined by storing the current protocol state in 
+-- `prev_state_s` and branching in `jump_state_c` after the data was 
+-- sent in and verified.
+--
+-- The basic protocol flow after `init` is:
+--  * `strt_state_c`: `cmd_do_start_c`: 75 or more SPI clocks to 
+--    enable SPI mode.
+--  * `idle_state_c`: `cmd_go_idle_state_c`: Put the card into idle 
+--    mode.
+--  * `init_state_c`: `cmd_send_op_cond_c`: Initialize the card so it 
+--    accepts commands.
+--  * `bsiz_state_c`: `cmd_set_blocklen_c`: Enforce a blocklen of 512 
+--    Byte, just in case.
+-- 
+-- Because the `cmd_go_idle_state_c` has to be sent multiple times 
+-- until `idled` occurs, there is another branch state `loop_state_c`
+-- for a simpler lookup table in `jump_state_c`.
+-- 
+-- After initialization, the `address`ed block can be read as 
+-- described in `sd_host`.  To handle the data, there is the fourth
+-- kind of states, the data handling.  These make sure, first the gap 
+-- between command execution and data retrieval is possible
+-- (`seek_state_c`), then the data is shifted out (`pipe_state_c`)
+-- and finally the 16 bit CRC is skipped (`skip_state_c').
+--
+-- Finally the fourth kind of states those handling the `sd_parser_e`:
+-- `send_state_c` sets the `trigger` and puts the (idle) `sd_parser_e`
+-- into `shifting` state.  Once it has shifted out all data (based on
+-- `command` and `argument`), `shft_state_c` jumps to `vrfy_state_c`
+-- which determines if an `error` or timeout occurred.  It then 
+-- proceeds in `jump_state_c` either with the protocol or back to 
+-- `rset_state_c`.
+--
 -----------------------------------------------------------------------
 -- This entity is part of the following library:
 -- pragma library fhw_sd
@@ -63,11 +136,19 @@ architecture rtl of sd_flow_e is
   signal prev_state_s : state_t;
   signal next_state_s : state_t;
 begin
+  -- CS (low active) has to be high in `rset_state_c` and until 
+  -- `strt_state_c` is finished.
   resetted <= '1' when curr_state_s = rset_state_c
          else '1' when curr_state_s = strt_state_c
          else '1' when prev_state_s = strt_state_c
          else '0';
+  -- The `sd_host` is `ready` when it is waiting for the `start`.
+  ready    <= '1' when curr_state_s = wait_state_c else '0';
+  -- The `sd_parser_e` is started by `send_state_c`.
+  trigger  <= '1' when curr_state_s = send_state_c else '0';
   
+  -- The state flow, branching is done via the signal `next_state_s`
+  -- which is set in the `branch` process below.
   sequence : process(clock, reset)
   begin
     if reset = '1' then
@@ -93,6 +174,10 @@ begin
     end if;
   end process;
   
+  -- Assign the correct `command` and `argument` cased on the current
+  -- (protocol) state.  If these are set, the state is stored in
+  -- `prev_state_s`.  The latter signal doesn't need a reset because
+  -- it can't be read before it was set.
   output : process(clock, reset)
   begin
     if reset = '1' then
@@ -131,23 +216,29 @@ begin
     end if;
   end process;
   
-  ready   <= '1' when curr_state_s = wait_state_c else '0';
-  trigger <= '1' when curr_state_s = send_state_c else '0';
-  
-  branch : process(clock)
+  -- All branching is based on `next_state_s` handled here.
+  branch : process(clock, reset)
   begin
-    if rising_edge(clock) then
+    if reset = '1' then
+      next_state_s <= rset_state_c;
+    elsif rising_edge(clock) then
       case curr_state_s is
+        -- `rset_state_c` loops until `init` occurs.
         when rset_state_c =>
           if init = '1' then
             next_state_s <= strt_state_c;
           end if;
+        -- `send_state_c` doesn't branch but we have to make 
+        -- `shft_state_c` loop and set `next_state_s` here.
         when send_state_c =>
           next_state_s <= shft_state_c;
+        -- `shft_state_c` loops until the `sd_parser_e` is done
         when shft_state_c =>
           if shifting = '0' then
             next_state_s <= vrfy_state_c;
           end if;
+        -- If an `error` occurred, we reset the card, else proceed 
+        -- with the protocol based on `prev_state_s`.
         when vrfy_state_c =>
           if error = '0' then
             case prev_state_s is
@@ -164,16 +255,21 @@ begin
           else
             next_state_s <= rset_state_c;
           end if;
+        -- If we aren't `idled` yet, we've got to send an 
+        -- `cmd_send_op_cond_c` again.  Else we can proceed.with the
+        -- protocol.
         when init_state_c =>
           if idled = '1' then
             next_state_s <= send_state_c;
           else
             next_state_s <= bsiz_state_c;
           end if;
+        -- `wait_state_c` loops until it is told to `start` reading.
         when wait_state_c =>
           if start = '1' then
             next_state_s <= read_state_c;
           end if;
+        -- The other states do depend on this signal.
         when others =>
           null;
       end case;
index 18b6317..0a526cf 100644 (file)
@@ -1,8 +1,10 @@
 -----------------------------------------------------------------------
 -- Copyright (c) 2009 Malte S. Stretz <http://msquadrat.de> 
 --
--- Some SD related types.
-
+-- This package collects a lot of the internal logic of the SD parser
+-- and state machine.  Some of this stuff should be moved to the 
+-- entities where it is used.
+-- 
 -----------------------------------------------------------------------
 -- This entity is part of the following library:
 -- pragma library fhw_sd
@@ -17,14 +19,24 @@ use ieee.numeric_std.all;
 
 package sd_globals_p is
 
-  constant block_address_width_c : positive := 23; -- max 4 GiB of 512 B blocks
+  -- A traditional (non-SDHC) SD card can address 32 bit (4 GiB) of 
+  -- data.  As each block is 512 Byte (9 bit) big, we need 23 bit to
+  -- address all blocks.
+  constant block_address_width_c : positive := 23;
   subtype  block_address_width_t is integer range 1 to block_address_width_c;
   subtype  std_logic_block_address_t is std_logic_vector(block_address_width_c - 1 downto 0);
 
+  -- SD commands are 6 bit wide (the upper two bits are constant start
+  -- bits), arguments 32 bit.  The (R1) response is 7 bit wide (the 
+  -- highest bit is a constant start bit again).
   subtype  std_logic_cmd_t is std_logic_vector(5 downto 0);
   subtype  std_logic_arg_t is std_logic_vector(31 downto 0);
   subtype  std_logic_rsp_t is std_logic_vector(6 downto 0);
   
+  -- We use two types of commands:  Standard SD commands and internal
+  -- control commands.  We can use the sixth bit as we only implement
+  -- SD commands up to 17.  A bunch of functions abstracts this 
+  -- detail so we can change it easily if we have to.
   constant cmd_type_std_c : std_logic := '0';
   constant cmd_type_int_c : std_logic := '1';
   function to_cmd(
@@ -37,9 +49,15 @@ package sd_globals_p is
   function is_int_cmd(
     cmd : std_logic_cmd_t) return std_logic;
   
+  -- Converts an unsigned 32 bit integer to the corresponding
+  -- argument bit vector.
   function to_arg(
     val : integer range 0 to 65535) return std_logic_arg_t;
   
+  -- An SD (command) frame is 6 Byte, ie. 48 bit, big.  The last byte
+  -- is constant and consists of a 7 bit CRC and a stop bit.  As we
+  -- don't use the CRC, only CMD0 needs a CRC and is ignored for all
+  -- other commands so we can just use the same value for everything.
   subtype  std_logic_frame_t is std_logic_vector(47 downto 0);
   subtype  std_logic_crc7_t  is std_logic_vector(6 downto 0);
   function create_frame(
@@ -49,13 +67,14 @@ package sd_globals_p is
     frame : std_logic_frame_t) return std_logic_frame_t;
   function get_frame_head(
     frame : std_logic_frame_t) return std_logic_byte_t;
-  
-  constant arg_null_c : std_logic_arg_t := (others => '0');
-  constant arg_ones_c : std_logic_arg_t := (others => '1');
-  
   constant crc_c : std_logic_crc7_t := "1001010";
   constant pad_c : std_logic_byte_t := (others => '1');
+  constant arg_null_c : std_logic_arg_t := (others => '0');
+  constant arg_ones_c : std_logic_arg_t := (others => '1');
 
+  -- The highest value our counter has to support are 512 Byte of 
+  -- data.  The top value is determined based on the current command
+  -- and its argument.
   constant counter_max_c : positive := 512;
   subtype  counter_top_t is integer range 0 to counter_max_c;
   function create_counter_top(
@@ -66,49 +85,64 @@ end sd_globals_p;
 
 package body sd_globals_p is
 
+  -- Create a SD command based on the type and the command index.
   function to_cmd(
     typ : std_logic;
     idx : integer range 0 to 31) return std_logic_cmd_t is
     variable cmd : std_logic_cmd_t;
   begin
+    -- As we use '1' to encode internal commands, we can simply
+    -- concat the bytes.
     cmd := typ & std_logic_vector(to_unsigned(idx, std_logic_cmd_t'length - 1));
     return cmd;
   end to_cmd;
   
+  -- Determine wether the command is an internal one or a real SD 
+  -- command.
   function get_cmd_type(
     cmd : std_logic_cmd_t) return std_logic is
   begin
+    -- The highest bit encodes the type.
     return cmd(std_logic_cmd_t'high);
   end get_cmd_type;
+  -- Determine if the command is an SD command.
   function is_std_cmd(
     cmd : std_logic_cmd_t) return std_logic is
   begin
     return not get_cmd_type(cmd);
   end is_std_cmd;
+  -- Determine if the command is an internal command.
   function is_int_cmd(
     cmd : std_logic_cmd_t) return std_logic is
   begin
     return get_cmd_type(cmd);
   end is_int_cmd;
   
+  -- Encode an unsigned 32 bit number as a bit vector.
   function to_arg(
     val : integer range 0 to 65535) return std_logic_arg_t is
   begin
     return std_logic_vector(to_unsigned(val, std_logic_arg_t'length));
   end to_arg;
   
+  -- Create a command frame from the command, the argument and the 
+  -- constant bits.
   function create_frame(
     cmd : std_logic_cmd_t;
     arg : std_logic_arg_t) return std_logic_frame_t is
     variable frame_v : std_logic_frame_t;
   begin
     if get_cmd_type(cmd) = cmd_type_std_c then
+      -- Prepend the start bits and add the CRC and the stop bit.
       frame_v := "01" & cmd & arg & crc_c & "1";
     else
+      -- Internal commands always output a constant high bit stream.
       frame_v := (others => '1');
     end if;
     return frame_v;
   end create_frame;
+  -- Once a byte is sent, we shift the frame buffer left and pad it 
+  -- with high bits.
   function shift_frame(
     frame : std_logic_frame_t) return std_logic_frame_t is
     variable frame_v : std_logic_frame_t;
@@ -116,6 +150,7 @@ package body sd_globals_p is
     frame_v := frame(std_logic_frame_t'high - 8 downto 0) & pad_c;
     return frame_v;
   end shift_frame;
+  -- Just return the high byte of the frame buffer.
   function get_frame_head(
     frame : std_logic_frame_t) return std_logic_byte_t is
     variable head_v : std_logic_byte_t;
@@ -124,14 +159,19 @@ package body sd_globals_p is
     return head_v;
   end get_frame_head;
 
+  -- Determine the top value of the counter based on command and 
+  -- argument.
   function create_counter_top(
     cmd : std_logic_cmd_t;
     arg : std_logic_arg_t) return counter_top_t is
     variable cnt_v : counter_top_t;
   begin
     if get_cmd_type(cmd) = cmd_type_std_c then
+      -- R1 commands are always six Byte long, followed by an optional
+      -- break of up to eight Byte and the final response byte.
       cnt_v := std_logic_cmd_t'length + 8 + 1;
     else
+      -- Internal commands encode their maximum length in the argument.
       cnt_v := to_integer(unsigned(arg));
     end if;
     return cnt_v;
index 15fc669..4c7c6e7 100644 (file)
@@ -1,9 +1,42 @@
 -----------------------------------------------------------------------
 -- Copyright (c) 2009 Malte S. Stretz <http://msquadrat.de> 
 --
--- TODO: description
--- NOTE: No SDHC (max. 4 GiB)
-
+-- This SD host is capable of reading blocks a 512 Byte from first
+-- generation standard Secure Digital (SD) cards (version 1 of the 
+-- standard, up to 4 GiB).  The later extensions SDHC and SDXC are not 
+-- supported.
+--
+-- It depends on (and uses) the SPI bus implemented in `fhw_sd`.  The 
+-- SPI signals `miso`, `mosi`, `sck`, and `cs` must be connected to
+-- card's pins `DAT`, `CMD`, `CLK`, and `DAT3`, respectively.
+--
+-- The flash devices can be accessed with to a theoretical speed of
+-- 900 kB/s and more but the initialization has to be done at a lower 
+-- speed of max 50 kB/s, ie. an SPI clock of 400 kHz.  This master uses
+-- a constant clock divider given by the generic `clock_divider`
+-- (relative to `clock_interval`) and doesn't increase the speed after
+-- initialization is done.
+-- 
+-- The entity is diven by the clock `clk` and the reset `rst` is high 
+-- active.
+--
+-- After reset, the SD card can be inserted and initialized by driving
+-- `init` high.  Once the card is successfully initialized (which can
+-- take a while), `ready` goes high.  If an error occurs, `error` is
+-- high and the SD error can be read from `rxd`.
+--
+-- The block address has to be applied to `address` and once the cards
+-- is `ready`, the addressed block can be read by driving `start` high.
+-- `address has to be stable for six clocks, then the next address can
+-- be set.  `start` may stay high.
+--
+-- Data is shifted out via `rxd`, each byte signaled by `shd`.  `rxd`
+-- is stable until the next byte arrives or the next command is sent.
+-- Between commands the output is undefined and can change arbitrarly.
+--
+-- If an error occurs, `error` goes high and the card is reset and has
+-- to be reinitialized.
+-- 
 -----------------------------------------------------------------------
 -- This entity is part of the following library:
 -- pragma library fhw_sd
@@ -106,6 +139,7 @@ architecture rtl of sd_host is
       sck   : out std_logic);
   end component;
   
+  -- These signals connect the two helper entities to each other.
   signal sd_command_s  : std_logic_cmd_t;
   signal sd_argument_s : std_logic_arg_t;
   signal sd_trigger_s  : std_logic;
@@ -113,6 +147,7 @@ architecture rtl of sd_host is
   signal sd_error_s    : std_logic;
   signal sd_idled_s    : std_logic;
   
+  -- These signals are the SPI bus management.
   signal spi_start_s : std_logic;
   signal spi_busy_s  : std_logic;
   signal spi_txd_s   : std_logic_byte_t;
@@ -120,16 +155,18 @@ architecture rtl of sd_host is
   signal spi_cs_s    : std_logic;
   
 begin
+  -- Direct output.
   rxd <= spi_rxd_s;
-  
+  -- Error is branched out as well.
   error <= sd_error_s;
   
+  -- The command flow state machine.
   driver : sd_flow_e port map(
     clock => clk,
     reset => rst,
     
     init    => init,
-    ready => ready,
+    ready   => ready,
     
     address => address,
     start   => start,
@@ -142,6 +179,7 @@ begin
     idled    => sd_idled_s,
     resetted => spi_cs_s);
   
+  -- The command parser and data shifter.
   parser : sd_parser_e port map(
     clock => clk,
     reset => rst,
@@ -160,6 +198,7 @@ begin
     spi_txd   => spi_txd_s,
     spi_rxd   => spi_rxd_s);
   
+  -- The SPI master.
   cs <= spi_cs_s;
   spi : spi_master port map(
     clk => clk,
index 87ad336..c37e5bb 100644 (file)
@@ -1,8 +1,63 @@
 -----------------------------------------------------------------------
 -- Copyright (c) 2009 Malte S. Stretz <http://msquadrat.de> 
 --
--- TODO: description
-
+-- Command and SPI parser state machine.
+-- 
+-- This helper entity parsers the SD/SPI protocol in combination
+-- with the `sd_flow_e`.
+--
+-- The following ports are connected directly to the `sd_host` ports:
+--  * `clock`: input, from `clk`
+--  * `reset`: input, from `rst`
+--  * `error`: output
+--  * `pipe`: output, to `shd`
+--  * `spi_rxd`: input, also `rxd`
+--
+-- The following ports are connected to the `sd_flow_e` ports:
+--  * `command`: input, SD command
+--  * `argument`: input, SD command argument
+--  * `trigger`: input, start `sd_parser_e`
+--  * `shifting`: output, `sd_parser_e` busy
+--  * `error`: output, SD error occurred
+--  * `idled`: output, SD idle mode
+--
+-- The following port are connected to the `spi_master` ports without
+-- the `spi_` prefix:
+--  * `spi_start`
+--  * `spi_busy`
+--  * `spi_txd`
+--  * `spi_rxd`
+-- 
+-- A relatively simple state machine with a variable `sd_counter_e`
+-- is `trigger`ed by the `sd_flow_e` protocol state machine to send
+-- a SD frame built from `command` and `argument`.
+-- 
+-- There are two kinds of commands:  Real SD commands and internal
+-- ones.  They are listed in `sd_commands_p` and their details are 
+-- described in `sd_globals_p`.  Real commands are shifted out as-is
+-- while internal commands carry the (maximum) number of bytes to
+-- receive in their argument and shift out a steady flow of high bits.
+-- 
+-- The `sd_counter_e` `top` value is used to count the bytes while 
+-- `shifting`.
+--
+-- An initial 6 Byte frame is built when the state machine pulled 
+-- from looping in `idle_state_c` to `load_state_c` by the `trigger`.
+-- Implementing the variable part of the SD protocol is easy by 
+-- padding the frame with all-ones for each shifted byte and waiting
+-- on the right end token based on the current `command`.
+-- 
+-- In `load_state_c` the 'shifting' is started and as `spi_txd` always 
+-- points to the head byte of the frame, the SPI master can be started#
+-- via `spi_start` in `send_state_c`. While the SPI master is busy 
+-- (`spi_busy`), the state machine loops in `shft_state_c`.
+--
+-- After each Byte, `vrfy_state_c` checks if a stop token was seen or
+-- an error or a timeout occurred, shifts, and branches either to the 
+-- next `send_state_c` or back to `idle_state_c`.
+--
+-- `command` and `argument` have to be stable while `shifting`.
+-- 
 -----------------------------------------------------------------------
 -- This entity is part of the following library:
 -- pragma library fhw_sd
@@ -75,44 +130,69 @@ architecture rtl of sd_parser_e is
   signal break_s : std_logic;
   signal error_s : std_logic;
 begin
+  -- The entity is busy when it isn't idle (duh).
   shifting <= '0' when state_s = idle_state_c
          else '1';
+  -- Hand out the error.
   error    <= error_s;
+  -- The idle flag is the lowest bit of the R1 answer of standard commands.
   idled    <= is_std_cmd(command)
           and spi_rxd(0);
+  -- Shifting is done with the internal command `cmd_do_pipe_c` (the 
+  -- `sd_host`'s `rxd` is directly wired to `spi_rxd`).
   pipe     <= '1' when command = cmd_do_pipe_c
                    and state_s = loop_state_c
          else '0';
   
+  -- Because `command` and `argument` are required to be stable, we can
+  -- feed the `top` of the counter directly.
   cnt_top_s  <= create_counter_top(command, argument);
+  -- But we have to reset the counter when done because some commands 
+  -- have a variable response time.
   cnt_rwnd_s <= '1' when state_s = idle_state_c
            else '0';
+  -- The counter is increased not only on `send_state_c` but also in 
+  -- the first `load_state_c` so we don't have to count to N-1.
   cnt_tick_s <= '1' when state_s = load_state_c
            else '1' when state_s = send_state_c
            else '0';
   
+  -- Directly wired to the first byte of the frame.
   spi_txd   <= get_frame_head(frame_s);
+  -- The SPI master is triggered in `send_state_c`.
   spi_start <= '1' when state_s = send_state_c
           else '0';
   
+  -- Detects a response token.  `spi_rxd` is high while the SD card is
+  -- working and R1s are announced with a low high bit.  The Data Token
+  -- on the other hand is announced by a low low bit.  All other 
+  -- internal commands have a fixed size.
   break_s <= not spi_rxd(7) when get_cmd_type(command) = cmd_type_std_c
         else not spi_rxd(0) when command = cmd_do_seek_c
         else '0';
   
+  -- There are several ways the `done_s` flag can be set.
   status : process(clock, reset)
   begin
     if reset = '1' then
       done_s <= '0';
     elsif rising_edge(clock) then
       case state_s is
+        -- The flag is reset in `idle_state_c`.
         when idle_state_c => done_s <= '0';
+        -- The counter is increased in `send_state_c`, so the 
+        -- `cnt_done_s` (aka timeout) can appear on the first `clock`
+        -- of the `shft_state_c`.
         when shft_state_c => done_s <= done_s or cnt_done_s;
+        -- Else we might have detected a response token.
         when vrfy_state_c => done_s <= done_s or break_s;
+        -- All other states must not set this flag.
         when others       => null;
       end case;
     end if;
   end process;
   
+  -- The SD frame has to be set and shifted.
   framer : process(clock, reset)
   begin
     if reset = '1' then
@@ -126,13 +206,19 @@ begin
     end if;
   end process;
   
+  -- In `vrfy_state_c` we have to determine if an `error` occurred.
   parser : process(clock, reset)
   begin
     if reset = '1' then
       error_s <= '0';
     elsif rising_edge(clock) then
+      -- We've got to test both signals because `break_s` will be 
+      -- joined with `done_s` just after this state.
       if state_s = vrfy_state_c and (done_s or break_s) = '1' then
+        -- Did we see a response token?
         if break_s = '1' then
+          -- Any bit except the highest and the lowest (idle) indicates
+          -- an error for a R1 response.
           if get_cmd_type(command) = cmd_type_std_c then
             error_s <= spi_rxd(6)
                     or spi_rxd(5)
@@ -140,12 +226,17 @@ begin
                     or spi_rxd(3)
                     or spi_rxd(2)
                     or spi_rxd(1);
+          -- The high bit will be set in an Data Error Token.
           elsif command = cmd_do_seek_c then
             error_s <= not spi_rxd(7);
+          -- Nah, everything is fine.
           else
             error_s <= '0';
           end if;
+        -- If we didn't see a response, there must have been a timeout.
         else
+          -- Most internal commands actually can't time out but send 
+          -- a fixed number of bytes.
           if get_cmd_type(command) = cmd_type_std_c then
             error_s <= '1';
           elsif command = cmd_do_seek_c then
@@ -158,26 +249,34 @@ begin
     end if;
   end process;
   
+  -- There's very little branching in this state machine.
   sequence : process(clock, reset)
   begin
     if reset = '1' then
       state_s <= idle_state_c;
     elsif rising_edge(clock) then
       case state_s is
+        -- A `trigger` starts the state machine.
         when idle_state_c =>
           if trigger = '1' then
             state_s <= load_state_c;
           end if;
+        -- After the frame was built, we always send it.
         when load_state_c =>
           state_s <= send_state_c;
+        -- `send_state_c` is just a trigger for the SPI master, go on.
         when send_state_c =>
           state_s <= shft_state_c;
+        -- When the SPI master is finished, it isn't `spi_busy`.
         when shft_state_c =>
           if spi_busy = '0' then
             state_s <= vrfy_state_c;
           end if;
+        -- This state just verifies, the next branches.
         when vrfy_state_c =>
           state_s <= loop_state_c;
+        -- Based on what `vrfy_state_c` found out, we either send 
+        -- another byte or are done.
         when loop_state_c =>
           if done_s = '1' then
             state_s <= idle_state_c;