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)
