For my part, I downloaded the docs for the SPI PROM, read them, drew
some diagrams, etc.  Now that I have it pretty much figured out, I'd
like to code it up.

To begin with, I developed a simplified simulation model of the
following SPI controller:

http://www.sst.com/downloads/datasheet/S71242.pdf

Here's the simulation model:

module spi_prom_sim(
   SI,
   SO,
   CE_,
   SCK
);

input SI, CE_, SCK;
output SO;
reg SO;


reg [255:0] data;
reg [26:0] addr;
reg [7:0] command;
reg out_en;

always @(negedge SCK) begin
   if (out_en) begin
       SO <= 1'bx;
       SO <= #3 data[addr[7:0]];
   end else begin
       SO <= 1'bz;
   end
end

reg [5:0] bit_counter;

parameter s_idle = 0;
parameter s_cmd = 1;
parameter s_addr = 2;
parameter s_write_data = 3;
parameter s_read_data = 4;

reg [3:0] state;

initial begin
   data = 'hdeadbeefcafebabe;
   state = 0;
   addr = 0;
   command = 0;
   bit_counter = 0;
   out_en = 0;
end

always @(negedge CE_) begin
   state = s_cmd;
   bit_counter = 7;
end

always @(posedge CE_) begin
   out_en = 0;
   state = s_idle;
   SO = #5 1'bz;
end

always @(posedge SCK) begin
   case (state)
       s_idle: ;
       s_cmd: begin
           command[bit_counter] = SI;
           bit_counter <= bit_counter - 1;
           if (!bit_counter) begin
               state = s_addr;
               addr = 0;
               bit_counter <= 26;
           end
       end
       s_addr: begin
           addr[bit_counter] = SI;
           bit_counter <= bit_counter - 1;
           if (bit_counter == 3) begin
               bit_counter <= 7;
               case (command)
                   'h03: begin
                       state = s_read_data;
                       out_en = 1;
                   end
                   'h0b: begin
                       addr = addr - 8;
                       state = s_read_data;
                       out_en = 1;
                   end
                   'h02: state = s_write_data;
                   default: state = s_idle;
               endcase
           end
       end
       s_write_data: begin
           data[addr[7:0]] = SI;
           bit_counter <= bit_counter - 1;
           addr = addr + 1;
           if (bit_counter == 0) state = s_idle;
       end
       s_read_data: begin
           addr = addr + 1;
       end
   endcase
end

endmodule


[Note:  I'm assuming, for simulation, a 20ns clock period.  Hense the
#3 in there.  This way, I can visually distinguish the transitions
between bits.]


The objective is to develop a controller that can read, write, and
execute other arbitrary commands.  Here's what the controller
interface should look like:

module spi_controller(
   clock,
   reset_,    // async negative reset

   // Read/write interface
   read_busy,    // Is the machine busy working on a read request?
   addr,    // Read or write address of 32-bit word
   write_data,   // Data to be written
   write_bytes,   // Byte enables of write data
   read_data,    // Data to be read
   read_out_valid,   // Does data in holding buffer correspond with read addr?
   do_read,   // Start a read
   do_write,    // Start a write

   // SPI interface
   SI,
   SO,
   CE_,
   SCK,

   // config
   cmd_read,    // 8-bit command code for read
   read_dummy_byte,   // Do I throw away 8 bits when reading?
   cmd_write   // 8-bit command code to write byte
);

input clock_1x, reset_;
output read_busy;
input [23:2] addr;
input [31:0] write_data;
input [3:0] write_bytes;
output [31:0] read_data;
output read_out_valid;
input do_read, do_write;

input SO;
output SI, CE_, SCK;

input [7:0] cmd_read, cmd_write;
input read_dummy_byte;


Here's the behavior:

The SPI PROM has a bunch of code words that are commands to tell the
chip what to do.  For read and write, we need to preconfigure them
somehow.  But for other commands, we'll have software set them.

Reset is async negative assertion:  always @(posedge clock or negedge
reset_) ...

read_busy indicates that the state machine cannot accept another read request.
On cycle 0, with read_busy=0, you can assert do_read.  If addr matches
the last address read from, on cycle 1, read_data is equal to the last
data read (read buffer), and read_out_valid=1.  If the address does
not match, on cycle 1, busy is asserted.  On cycle n, when the data is
ready, read_out_valid is asserted, and busy is deasserted.  While busy
is asserted, all other requests are ignored.  This way, both busy and
do_read signals can be registered.

Anything other than a read invalidates the read buffer.

do_write serves four functions.  If a 32-bit write
(write_bytes=='b1111) is made to an address where addr[3:2]==0, this
will cause an 8-bit special function.  This is so that the host can
send commands chip-erase, write-enable, write-disable, and EWSR.

If a 32-bit write is made to an address where addr[3:2]==1, this will
cause a 16-bit special function.  This is so that the host can send
the WRSR command.

If a 32-bit write is made to an address where addr[3:2]==2, this will
cause a 32-bit special function.  This is so that the host can send
commands sector-erase, block-erase, and chip-erase.

For the special modes, the ordering of bits in the data word can be
whatever is most convenient.

If an 8-bit write (write_bytes!='b1111) is made to any address, this
causes a byte write to the PROM.  The controller sends the write
command, the address, and the data byte.

For everything but reads, software is responsible for respecting the
proper timing delays bewtween commands.  For instance, while the state
machine is busy during a write operation, any other action is invalid
and the fault of the programmer if taken; software must wait the
appropriate 20 microseconds between write commands.


What follows is the rest of the source to the spi_test.v file:



module spi_test;

reg clock, _reset;

initial begin
   $dumpfile( "test.vcd" );
   $dumpvars;
   #3;
   _reset = 0;
   #101;
   _reset = 1;

   #5000;
   $finish;
end

initial begin
   clock = 1;
   forever begin
       #10;
       clock = !clock;
   end
end

reg [31:0] write_data;
reg [3:0] write_bytes;
wire [31:0] read_data;
wire read_valid, busy;
reg do_read, do_write;
reg [23:0] addr;

wire SI, SCK;
wire SO, CE_;


spi_controller sc (
   .clock_1x           (clock),
   .reset              (_reset),

   .busy               (busy),
   .addr               (addr[23:2]),
   .write_data         (write_data),
   .write_bytes        (write_bytes),
   .read_data          (read_data),
   .read_out_valid     (read_valid),
   .do_read            (do_read),
   .do_write           (do_write),

   .SI                 (SI),
   .SO                 (SO),
   .CE_                (CE_),
   .SCK                (SCK),

   .cmd_read           (8'h03),
   .read_dummy_byte    (1'b0),
   .cmd_write          (8'h02)
);


spi_prom_sim sps(
   .SI(SI),
   .SO(SO),
   .CE_(CE_),
   .SCK(SCK)
);


/* For testing the controller
initial begin
   #3;
   do_read = 0;
   do_write = 0;
   while (!_reset) #20;
   addr <= 8;
   write_data <= 'hCAFEBABE;
   write_bytes <= 'b0001;
   do_write = 1;
   #20
   do_write = 0;
   #1000;
   do_read = 1;
   while (busy) #20;
   #20;
   do_read = 0;
end
*/


/* For testing the simulation model
reg [31:0] cmd, data;
integer i;
initial begin
   cmd = 'h0b000000;
   CE_ = 1;
   SI = 0;
   SCK = 0;

   while (!_reset) #20;
   #20;

   CE_ = 0;
   for (i=0; i<32; i=i+1) begin
       SCK = 0;
       SI = 'bx;
       #3;
       SI = cmd[31];
       cmd = cmd << 1;
       #7;
       SCK = 1;
       #7;
       SI = 'bx;
       #3;
   end

   SI = 'bx;

   for (i=0; i<39; i=i+1) begin
       SCK = 0;
       #10;
       SCK = 1;
       data = {SO, data[31:1]};
       #10;
   end
   SCK = 0;
   #10;
   data = {SO, data[31:1]};
   CE_ = 1;
end
*/

endmodule
_______________________________________________
Open-graphics mailing list
[email protected]
http://lists.duskglow.com/mailman/listinfo/open-graphics
List service provided by Duskglow Consulting, LLC (www.duskglow.com)

Reply via email to