blob: 0394bbffc1a52a49d2dce30209071254bd4fb39f [file] [log] [blame]
`default_nettype none
`timescale 1ns/10ps
module spi_sram_encoder #(
parameter WORD_WIDTH = 16,
parameter ADDRESS_WIDTH = 16
) (
input wire clk,
input wire reset,
input wire request,
output wire busy,
output reg initialized,
// Parallel memory nets
input wire [ADDRESS_WIDTH-1:0] address,
input wire write_enable,
output reg [WORD_WIDTH-1:0] data_in,
input wire [WORD_WIDTH-1:0] data_out,
// Serial SRAM nets
output reg sram_cs_n,
output sram_sck,
output reg sram_sio_oe, // output enable the SIO lines
// SIO as inputs from SRAM
input wire sram_sio0_i, // sram_si_sio0
input wire sram_sio1_i, // sram_so_sio1
input wire sram_sio2_i, // sram_sio2
input wire sram_sio3_i, // sram_hold_n_sio3
// SIO as outputs to SRAM
output wire sram_sio0_o, // sram_si_sio0
output wire sram_sio1_o, // sram_so_sio1
output wire sram_sio2_o, // sram_sio2
output wire sram_sio3_o // sram_hold_n_sio3
);
// 23LC1024 Instruction codes
`define INS_READ 8'b0000_0011 // Read instruction
`define INS_WRMR 8'b0000_0001 // Write Mode Register instruction
`define INS_WRITE 8'b0000_0010 // Write instruction
`define INS_RDMR 8'b0000_0101 // Read Mode Register instruction
`define INS_EDIO 8'b0011_1011 // Enter Dual I/O instruction
`define INS_EQIO 8'b0011_1000 // Enter Quad I/O instruction
`define INS_RSTIO 8'b1111_1111 // Reset Dual and Quad I/O instruction
`define BYTEMODE 2'b00 // Byte operation mode
`define PAGEMODE 2'b10 // Page operation mode
`define SEQMODE 2'b01 // Sequential operation mode
`define SPIMODE 2'b00 // SPI I/O mode
`define SDIMODE 2'b01 // SDI I/O mode
`define SQIMODE 2'b10 // SQI I/O mode
function [31:0] max3(input [31:0] x, y, z);
begin
max3 = x > y ? (x > z ? x :z) : (y > z ? y : z);
end
endfunction
// 23LC1024 Address width
localparam SRAM_ADDRESS_WIDTH = 24;
localparam SRAM_INSTRUCTION_WIDTH = 8;
localparam OUTPUT_BUFFER_WIDTH = max3(SRAM_ADDRESS_WIDTH, SRAM_INSTRUCTION_WIDTH ,WORD_WIDTH);// 24;
localparam INPUT_BUFFER_WIDTH = WORD_WIDTH;
localparam INPUT_DUMMY_WIDTH = 8;
localparam BITS_PER_CLK = 4;
// states
localparam [2:0]
state_idle = 3'b000,
state_start = 3'b001,
state_instruction = 3'b010,
state_address = 3'b011,
state_read = 3'b100,
state_write = 3'b101,
state_reset = 3'b110,
state_set_SQI_mode = 3'b111
;
reg [2:0] current_state;
reg [4:0] initializing_step;
reg [ADDRESS_WIDTH-1:0] request_address;
reg [WORD_WIDTH-1:0] request_data_out;
reg request_write;
reg [OUTPUT_BUFFER_WIDTH-1:0] output_buffer;
reg [$clog2(OUTPUT_BUFFER_WIDTH)-1:0] output_bits_left;
reg [INPUT_BUFFER_WIDTH-1:0] input_buffer;
reg [$clog2(INPUT_BUFFER_WIDTH + INPUT_DUMMY_WIDTH)-1:0] input_bits_left;
reg toggled_sram_sck;
assign sram_sck = ~sram_cs_n & toggled_sram_sck;
// aliases
// wire sio0 = sram_si_sio0;
// wire sio1 = sram_so_sio1;
// wire sio2 = sram_sio2;
// wire sio3 = sram_hold_n_sio3;
// wire [3:0] sio_o;
wire [3:0] sio_i = {sram_sio3_i, sram_sio2_i, sram_sio1_i, sram_sio0_i};
wire [3:0] sio_o = output_buffer[OUTPUT_BUFFER_WIDTH-1:OUTPUT_BUFFER_WIDTH-4];
// assign sio_o = next_output_data;
assign {sram_sio3_o, sram_sio2_o, sram_sio1_o, sram_sio0_o} = sio_o;
assign busy = (current_state!=state_idle);
always @(posedge clk) begin
if (reset) begin
// reset
current_state <= state_reset;
initialized <= 1'b0;
sram_cs_n <= 1'b1;
sram_sio_oe <= 1'b1;
initializing_step <= 0;
// set output to 1 to keep HOLD_N high during initialization
output_buffer[OUTPUT_BUFFER_WIDTH-1:OUTPUT_BUFFER_WIDTH-4] <= 4'b1111;
input_buffer <= 0;
data_in <= 0;
toggled_sram_sck <= 0;
end else begin
toggled_sram_sck <= ~toggled_sram_sck;
// Active transfer states, hapenning at the falling edge sram_clk
if(toggled_sram_sck==1'b1) begin
/* verilator lint_off CASEINCOMPLETE */
case (current_state)
state_reset:
begin
if(sram_cs_n==1'b1)
sram_cs_n <= 1'b0;
initializing_step <= initializing_step + 1;
// Send reset instruction
case (initializing_step)
0: output_buffer <= {`INS_RSTIO, {(OUTPUT_BUFFER_WIDTH-SRAM_INSTRUCTION_WIDTH){1'b0}}};
1: output_buffer <= output_buffer << BITS_PER_CLK;
default:
begin
current_state <= state_set_SQI_mode;
sram_cs_n <= 1'b1;
initializing_step <= 0;
end
endcase
end
state_set_SQI_mode:
begin
// Enable device
if(sram_cs_n==1'b1)
sram_cs_n <= 1'b0;
initializing_step <= initializing_step + 1;
// Output INS_EQIO (8'b0011_1000): Enter Quad I/O instruction
case (initializing_step)
0: output_buffer[20] <= 0;
1: output_buffer[20] <= 0;
2: output_buffer[20] <= 1;
3: output_buffer[20] <= 1;
4: output_buffer[20] <= 1;
5: output_buffer[20] <= 0;
6: output_buffer[20] <= 0;
7: output_buffer[20] <= 0;
8: begin
current_state <= state_idle;
sram_cs_n <= 1'b1;
initialized <= 1'b1;
end
endcase
end
state_idle:
begin
if(request && !busy) begin
current_state <= state_start;
// save request
request_address <= address;
request_write <= write_enable;
request_data_out <= data_out;
// Enable output GPIO
sram_sio_oe <= 1'b1;
end
end
state_start:
begin
// Enable device
sram_cs_n <= 1'b0;
current_state <= state_instruction;
// fill the buffer with the instruction data
if(request_write) begin
output_buffer <= {`INS_WRITE, {(OUTPUT_BUFFER_WIDTH-SRAM_INSTRUCTION_WIDTH){1'b0}}};
output_bits_left <= SRAM_INSTRUCTION_WIDTH;
end else begin
output_buffer <= {`INS_READ, {(OUTPUT_BUFFER_WIDTH-SRAM_INSTRUCTION_WIDTH){1'b0}}};
output_bits_left <= SRAM_INSTRUCTION_WIDTH;
end
end
state_instruction:
begin
// Last bits of instruction?
if(output_bits_left == BITS_PER_CLK) begin
current_state <= state_address;
// Load Address
// As we read 2 bytes for every HACK memory address, we multiply by 2 before sending it to the 23LC1024
output_buffer <= { {(OUTPUT_BUFFER_WIDTH-ADDRESS_WIDTH-1){1'b0}}, request_address[ADDRESS_WIDTH-1:0] , 1'b0};
output_bits_left <= SRAM_ADDRESS_WIDTH;
end else begin
// Output next bits
output_buffer <= output_buffer << BITS_PER_CLK;
output_bits_left <= output_bits_left - BITS_PER_CLK;
end
end
state_address:
begin
// Last bits of address?
if(output_bits_left == BITS_PER_CLK) begin
if(request_write) begin
current_state <= state_write;
// Load output data
output_buffer <= {request_data_out, {(OUTPUT_BUFFER_WIDTH-WORD_WIDTH){1'b0}}};
output_bits_left <= WORD_WIDTH;
end else begin
current_state <= state_read;
// Enable input GPIO
sram_sio_oe <= 1'b0;
input_bits_left <= INPUT_BUFFER_WIDTH + INPUT_DUMMY_WIDTH;
end
end else begin
// Output next bits
output_buffer <= output_buffer << BITS_PER_CLK;
output_bits_left <= output_bits_left - BITS_PER_CLK;
end
end
state_write:
begin
// Last bits of data?
if(output_bits_left == BITS_PER_CLK) begin
current_state <= state_idle;
// On write I should also output the value we just wrote
data_in <= request_data_out;
// sram deselected
sram_cs_n <= 1'b1;
end else begin
// Output next bits
output_buffer <= output_buffer << BITS_PER_CLK;
output_bits_left <= output_bits_left - BITS_PER_CLK;
end
end
state_read:
begin
input_buffer <= {input_buffer[INPUT_BUFFER_WIDTH-BITS_PER_CLK-1:0] , sio_i};
// Last bits of data?
if(input_bits_left == BITS_PER_CLK) begin
data_in <= {input_buffer[INPUT_BUFFER_WIDTH-BITS_PER_CLK-1:0] , sio_i};
current_state <= state_idle;
// sram deselected
sram_cs_n <= 1'b1;
end else begin
input_bits_left <= input_bits_left - BITS_PER_CLK;
end
end
endcase
/* lint_on */
end
end
end
`ifdef FORMAL
// register for knowing if we have just started
reg f_past_valid = 0;
// start in reset
initial assume(reset);
initial assume(current_state==0);
always @(posedge clk) begin
assume(address==16'hAED0);
f_past_valid <= 1;
// if(f_past_valid && !$past(reset) && !busy && $past(toggled_sram_sck==1)) begin
// request <= 1'b1;
// end else begin
// request <= 1'b0;
// end
if(!reset && sram_cs_n==1'b0) begin
ASSERT_BUSY: assert(busy);
end
if(current_state==state_read) begin
ASSERT_OUTPUT_DISABLED_ON_READ: assert(sram_sio_oe==0);
end
if(f_past_valid) begin
COVER_READ: cover($past(current_state)==state_read && current_state==state_idle);
COVER_WRITE: cover($past(current_state)==state_write && current_state==state_idle);
COVER_INITIALIZED: cover(initialized);
if(current_state!=state_reset && current_state!=state_start && current_state!=state_set_SQI_mode) begin
ASSERT_INITILIZED: assert(initialized);
end
if(sram_cs_n==1'b0 && toggled_sram_sck==1'b1) begin
ASSERT_SRAM_INPUT_SET_BEFORE_CLOCK:
assert($past(sio_o)==sio_o);
end
if(!$past(reset) && current_state==state_idle) begin
ASSERT_SRAM_OFF_ON_IDLE:
assert(sram_cs_n==1'b1);
if(!reset && $past(current_state)==state_write) begin
ASSERT_OUTPUT_EQUAL_INPUT_ON_WRITE: assert(data_in==request_data_out);
end
end
end
end
`endif
endmodule