blob: 0f843fb8d0862a3548f570ef70a776648de138a8 [file] [log] [blame]
`default_nettype none
`timescale 1ns/10ps
// Mienstras no esta leyendo la linea puede escribir. deberia cortas a tiempos para empezar antes que se necesite el primer dato
module spi_video_ram_2 #(
parameter WORD_WIDTH = 0,
parameter VRAM_ADDRESS_WIDTH = 0,
parameter SRAM_ADDRESS_WIDTH = 0,
parameter HACK_SCREEN_WIDTH = 0,
parameter HACK_SCREEN_HEIGHT = 0,
parameter HACK_SCREEN_H_OFFSET = 0,
parameter HACK_SCREEN_V_OFFSET = 0
)(
input wire clk,
input wire reset,
output reg initialized,
// VIDEO VRAM READ
input signed [9:0] clks_before_active,
input display_active,
input [9:0] display_hpos,
input [9:0] display_vpos,
output pixel_out,
// HACK VRAM WRITE
input hack_clk_rise_edge,
input [WORD_WIDTH-1:0] hack_outM,
input [VRAM_ADDRESS_WIDTH-1:0] hack_addressM,
input hack_writeM,
// Serial SRAM nets
output reg sram_cs_n,
output reg 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 reg sram_sio0_o, // sram_si_sio0
output reg sram_sio1_o, // sram_so_sio1
output reg sram_sio2_o, // sram_sio2
output reg 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
// 23LC1024 Address width
localparam signed CLKS_BEFORE_TRIGGER = 46; //42; // 26? if we use the fast version of the read instruction
// localparam SRAM_ADDRESS_WIDTH = 24;
localparam OUTPUT_BUFFER_WIDTH = SRAM_ADDRESS_WIDTH;
localparam INPUT_BUFFER_WIDTH = 4;
localparam SRAM_INSTRUCTION_WIDTH = 8;
localparam BITS_PER_CLK = 4;
`ifndef FORMAL
localparam INPUT_NIBBLES_PER_LINE = (HACK_SCREEN_WIDTH>>2);
`else
localparam INPUT_NIBBLES_PER_LINE = 3;
`endif
localparam QSI_INSTRUCTION_CLKS = 2;
localparam QSI_ADDRESS_CLKS = 6;
localparam QSI_READ_DUMMY_CLKS = 2;
localparam QSI_READ_LINE_CLKS = INPUT_NIBBLES_PER_LINE;
localparam QSI_WRITE_WORD_CLKS = WORD_WIDTH>>2;
localparam READ_LINE_TOTAL_CLKS = QSI_INSTRUCTION_CLKS + QSI_ADDRESS_CLKS + QSI_READ_DUMMY_CLKS + QSI_READ_LINE_CLKS + 1;
localparam WRITE_WORD_TOTAL_CLKS = QSI_INSTRUCTION_CLKS + QSI_ADDRESS_CLKS + QSI_WRITE_WORD_CLKS;
// states
localparam [2:0]
state_reset = 3'b000,
state_set_SQI_mode = 3'b001,
state_idle = 3'b010,
state_read = 3'b011,
state_write = 3'b100
;
wire fifo_empty;
wire fifo_full;
reg fifo_read_request;
wire [VRAM_ADDRESS_WIDTH-1:0] fifo_out_address;
wire [WORD_WIDTH-1:0] fifo_out_data;
reg fifo_write_request;
reg [VRAM_ADDRESS_WIDTH-1:0] fifo_in_address;
reg [WORD_WIDTH-1:0] fifo_in_data;
vram_write_fifo #(.DATA_WIDTH(16), .ADDRESS_WIDTH(16))
write_fifo (
.clk(clk),
.reset(reset),
.read_request(fifo_read_request),
.read_address(fifo_out_address),
.read_data(fifo_out_data),
.write_request(fifo_write_request),
.write_address(fifo_in_address),
.write_data(fifo_in_data),
// .items_count(),
.full(fifo_full),
.empty(fifo_empty)
// .overrun(),
// .underrun()
);
wire busy;
reg [2:0] current_state;
reg [$clog2(READ_LINE_TOTAL_CLKS*4):0] state_counter;
wire [1:0] transfer_mode;
reg [$clog2(READ_LINE_TOTAL_CLKS):0] state_sram_clk_counter;
reg sram_sck_rise_edge;
reg sram_sck_fall_edge;
reg [OUTPUT_BUFFER_WIDTH-1:0] output_buffer;
reg [$clog2(OUTPUT_BUFFER_WIDTH):0] buffer_index;
reg start_read;
wire is_active_hack_line = ( display_vpos >= HACK_SCREEN_V_OFFSET ) && (display_vpos< (HACK_SCREEN_V_OFFSET+HACK_SCREEN_HEIGHT));
wire is_active_hack_col = (display_hpos >= HACK_SCREEN_H_OFFSET) && (display_hpos < (HACK_SCREEN_H_OFFSET+HACK_SCREEN_WIDTH));
// wire display_trigger_read = is_active_hack_line && (clks_before_active==CLKS_BEFORE_TRIGGER);
wire display_trigger_read = initialized && is_active_hack_line && (clks_before_active==(CLKS_BEFORE_TRIGGER-HACK_SCREEN_H_OFFSET));
// wire write_to_sram_ready = !busy && !fifo_empty && ($signed(clks_before_active)>$signed(CLKS_BEFORE_TRIGGER-HACK_SCREEN_H_OFFSET+24) );
wire write_to_sram_ready = !busy && !fifo_empty ;
wire [SRAM_ADDRESS_WIDTH-1:0] line_read_address = {display_vpos-HACK_SCREEN_V_OFFSET, 6'b0};
wire [1:0] temp_pixel_index = ~(display_hpos[1:0]);
// Squares background:
// wire background = display_vpos[3] || display_hpos[3]
// Frame background:
// wire background = (
// (display_vpos==(HACK_SCREEN_V_OFFSET-1) & display_hpos[0]) ||
// (display_vpos==(HACK_SCREEN_HEIGHT+HACK_SCREEN_V_OFFSET) & ~display_hpos[0]) ||
// (display_hpos==(HACK_SCREEN_H_OFFSET-1) & display_vpos[0]) ||
// (display_hpos==(HACK_SCREEN_H_OFFSET+HACK_SCREEN_WIDTH) & ~display_vpos[0])
// );
wire background = 0;
assign pixel_out = display_active ?
(is_active_hack_line & is_active_hack_col) ? ~read_value[temp_pixel_index] : background
: 0;
assign busy = (current_state!=state_idle);
assign transfer_mode = current_state==state_set_SQI_mode ? `SPIMODE : `SQIMODE;
// ** current_state && state_counter && initialized ** //
always @(posedge clk ) begin
if(reset) begin
current_state <= state_reset;
state_counter <= 0;
initialized <= 0;
end else begin
state_counter <= state_counter + 1'b1;
case (current_state)
state_reset: begin
if(state_counter!=0 && state_sram_clk_counter == 2) begin
current_state <= state_idle;
state_counter <= 0;
end
end
state_set_SQI_mode: begin
if(state_sram_clk_counter == 8) begin
current_state <= state_idle;
state_counter <= 0;
initialized <= 1'b1;
end
end
state_idle: begin
if(!initialized) begin
current_state <= state_set_SQI_mode;
state_counter <= 0;
end else if(fifo_empty & start_read) begin
// end else if(start_read) begin
current_state <= state_read;
state_counter <= 0;
end else if(write_to_sram_ready) begin
current_state <= state_write;
state_counter <= 0;
end
end
state_read: begin
// Finish reading?
if(state_sram_clk_counter==READ_LINE_TOTAL_CLKS) begin
current_state <= state_idle;
state_counter <= 0;
end
end
state_write: begin
// Finish writing?
if(state_sram_clk_counter==WRITE_WORD_TOTAL_CLKS) begin
current_state <= state_idle;
state_counter <= 0;
end
end
default: begin
current_state <= current_state;
end
endcase
end
end
// ** sram_sio0_o, sram_sio1_o, sram_sio2_o, sram_sio3_o ** //
always @(output_buffer or buffer_index) begin
if(transfer_mode==`SQIMODE) begin
sram_sio3_o = output_buffer[buffer_index];
sram_sio2_o = output_buffer[buffer_index-1];
sram_sio1_o = output_buffer[buffer_index-2];
sram_sio0_o = output_buffer[buffer_index-3];
end else begin
sram_sio3_o = 1'b1; // <<< HOLD_N = 1
sram_sio2_o = 1'b0;
sram_sio1_o = 1'b0;
sram_sio0_o = output_buffer[buffer_index];
end
end
// ** start_read ** //
always @(posedge clk ) begin
if(reset) begin
start_read <= 0;
end else begin
if(!busy) begin
start_read <= display_trigger_read;
end
end
end
// ** fifo_read_request ** //
always @(posedge clk) begin
fifo_read_request <= 0;
if(current_state==state_idle && write_to_sram_ready) begin
fifo_read_request <= 1;
end
end
// ** fifo_write_request, fifo_in_data, fifo_in_address ** //
always @(posedge clk) begin
fifo_write_request <= 0;
if(hack_clk_rise_edge && hack_writeM && !fifo_full) begin
fifo_in_data <= hack_outM;
fifo_in_address <= hack_addressM;
fifo_write_request <= 1;
end
end
// ** sram_sio_oe ** //
always @(posedge clk ) begin
sram_sio_oe <= 1;
if(current_state==state_read) begin
if(state_sram_clk_counter>=10) begin
sram_sio_oe <= 0;
end
end
end
// ** output_buffer && sio ** //
always @(posedge clk ) begin
if(reset) begin
// Keep HOLD_N high during initialization
// sio <= 4'b1111;
end else begin
if(sram_sck_rise_edge) begin
if(transfer_mode==`SQIMODE) begin
buffer_index <= buffer_index - 4;
end else begin
buffer_index <= buffer_index - 1;
end
end
/* verilator lint_off CASEINCOMPLETE */
case (current_state)
state_reset: begin
if(state_sram_clk_counter==0) begin
output_buffer <= {`INS_RSTIO, {(OUTPUT_BUFFER_WIDTH-SRAM_INSTRUCTION_WIDTH){1'b0}}};
buffer_index <= OUTPUT_BUFFER_WIDTH-1;
end
end
state_set_SQI_mode: begin
if(state_sram_clk_counter==0) begin
output_buffer <= {`INS_EQIO, {(OUTPUT_BUFFER_WIDTH-SRAM_INSTRUCTION_WIDTH){1'b0}}};
buffer_index <= OUTPUT_BUFFER_WIDTH-1;
end
end
state_read: begin
if(state_sram_clk_counter==0) begin
// instruction
output_buffer <= {`INS_READ, {(OUTPUT_BUFFER_WIDTH-SRAM_INSTRUCTION_WIDTH){1'b0}}};
buffer_index <= OUTPUT_BUFFER_WIDTH-1;
end else if(state_sram_clk_counter==2) begin
// address
output_buffer <= { line_read_address , {(OUTPUT_BUFFER_WIDTH-SRAM_ADDRESS_WIDTH){1'b0}}};
buffer_index <= OUTPUT_BUFFER_WIDTH-1;
end
end
state_write: begin
if(state_sram_clk_counter==0) begin
// instruction
output_buffer <= {`INS_WRITE, {(OUTPUT_BUFFER_WIDTH-SRAM_INSTRUCTION_WIDTH){1'b0}}};
buffer_index <= OUTPUT_BUFFER_WIDTH-1;
end else if(state_sram_clk_counter==QSI_INSTRUCTION_CLKS) begin
// 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-VRAM_ADDRESS_WIDTH-1){1'b0}}, fifo_out_address , 1'b0};
buffer_index <= OUTPUT_BUFFER_WIDTH-1;
end else if(state_sram_clk_counter==(QSI_INSTRUCTION_CLKS + QSI_ADDRESS_CLKS)) begin
// data
// output_buffer <= { fifo_out_data, {(OUTPUT_BUFFER_WIDTH-WORD_WIDTH){1'b0}}};
// Temporal solution to invert the data word for screen, so we can read it in order while filling the screen (because HACK expects the first pixel to be on data[0] instead of data[15])
output_buffer <= {
fifo_out_data[0], fifo_out_data[1], fifo_out_data[2], fifo_out_data[3], fifo_out_data[4], fifo_out_data[5], fifo_out_data[6], fifo_out_data[7],
fifo_out_data[8], fifo_out_data[9], fifo_out_data[10], fifo_out_data[11], fifo_out_data[12], fifo_out_data[13], fifo_out_data[14], fifo_out_data[15],
{(OUTPUT_BUFFER_WIDTH-WORD_WIDTH){1'b0}}};
buffer_index <= OUTPUT_BUFFER_WIDTH-1;
end
end
endcase
/* verilator lint_on CASEINCOMPLETE */
end
end
reg [INPUT_BUFFER_WIDTH-1:0] read_value;
// ** input_buffer ** //
always @(posedge clk ) begin
if(current_state==state_read) begin
if(state_sram_clk_counter>=10 && sram_sck_fall_edge) begin
read_value <= {sram_sio3_i, sram_sio2_i, sram_sio1_i, sram_sio0_i};
end
end
end
// ** sram_cs_n ** //
always @(posedge clk) begin
if(reset) begin
sram_cs_n <= 1'b1;
end else begin
if(current_state!=state_idle) begin
sram_cs_n <= 1'b0;
end else begin
sram_cs_n <= 1'b1;
end
end
end
// ** sram_sck , state_sram_clk_counter, sram_sck_rise_edge, sram_sck_fall_edge ** //
always @(negedge clk) begin
if(sram_cs_n) begin
sram_sck <= 0;
state_sram_clk_counter <= 0;
sram_sck_rise_edge <= 0;
sram_sck_fall_edge <= 0;
end else begin
sram_sck_rise_edge <= 0;
sram_sck_fall_edge <= 0;
if(current_state==state_idle) begin
sram_sck <= 0;
state_sram_clk_counter <= 0;
// end else if(current_state!=state_read || state_sram_clk_counter < 9) begin
end else if(current_state!=state_read) begin
sram_sck <= state_counter[0];
sram_sck_fall_edge <= sram_sck && ~state_counter[0];
if(~sram_sck && state_counter[0]) begin
state_sram_clk_counter <= state_sram_clk_counter + 1;
sram_sck_rise_edge <= 1;
end
end else begin
sram_sck <= state_counter[0] ^ state_counter[1] ;
sram_sck_fall_edge <= sram_sck && ~(state_counter[0] ^ state_counter[1]);
if(~sram_sck && (state_counter[0] ^ state_counter[1])) begin
state_sram_clk_counter <= state_sram_clk_counter + 1;
sram_sck_rise_edge <= 1;
end
end
end
end
`ifdef FORMAL
localparam READ_TRIGGER_BEFORE_ACTIVE_CLKS = 26;
/*
//Full 640x480 data
localparam ACTIVE_LINES = 480;
localparam VA_END = ACTIVE_LINES;
localparam HCOUNT_MAX = 800;
localparam VCOUNT_MAX = 525;
localparam HA_STA = 160;
/*/
// Smaller values for testing
localparam ACTIVE_LINES = 2;
localparam VA_END = ACTIVE_LINES;
localparam HCOUNT_MAX = 20;
localparam VCOUNT_MAX = 3;
localparam HA_STA = 12;
//*/
reg f_past_valid = 0;
reg initial_reset_passed = 0;
reg [9:0] h_count;
reg [9:0] v_count;
reg [3:0] some_input_data = 'b1010;
initial begin
assume(reset);
assume(!initialized);
// assume(line_read_address==display_vpos*'h40);
// assume(display_trigger_read==0);
// assume(current_state==state_set_SQI_mode);
// assume(line_read_address=='h40);
// assume(h_count == 150);
end
// The clock toggles.
reg last_clk = 1'b0;
always @($global_clock) begin
assume (clk == !last_clk);
last_clk <= clk;
end
always @(negedge clk) begin
if(f_past_valid) begin
// if(reset) begin
// assume($past(reset));
// end
if(initial_reset_passed) begin
assume(reset==0);
end
end
end
always @(posedge clk) begin
if(~((h_count < HA_STA) | (v_count > ACTIVE_LINES - 1))) begin
assume(display_active);
end else begin
assume(display_active==0);
end
if((h_count+READ_TRIGGER_BEFORE_ACTIVE_CLKS == HA_STA) && (v_count <= ACTIVE_LINES - 1)) begin
// if(~(( (h_count+10) == 160) | (v_count > 480 - 1))) begin
assume(display_trigger_read) ;
end else begin
assume(display_trigger_read==0) ;
end
f_past_valid <= 1;
// if((h_count+READ_TRIGGER_BEFORE_ACTIVE_CLKS == HA_STA) && (v_count <= ACTIVE_LINES - 1)) begin
// // if(~(( (h_count+10) == 160) | (v_count > 480 - 1))) begin
// assume(display_trigger_read) ;
// end else begin
// assume(display_trigger_read==0) ;
// end
if(f_past_valid) begin
if($fell(reset)) begin
initial_reset_passed <= 1;
end
if(initial_reset_passed) begin
COVER_STATE_IDLE: cover(current_state==state_idle);
COVER_STATE_READ: cover(current_state==state_read);
COVER_FINISH_READ: cover($past(current_state)==state_read && current_state==state_idle);
end
end
end
`endif
endmodule