`include "defs.svh" `define RESET_CYCLES 5 // Spec wants 100ns `define TRWR_CYCLES 2 // Spec wants 40ns module ram_controller ( input bit clock , input bit reset , output bit command_ready , input bit command_valid , input ram_command_t command_data , input bit result_ready , output bit result_valid , output ram_read_response_t result_data , output bit ram_resetn , output bit ram_csn , output bit ram_clkp , output bit ram_clkn , output bit ram_rwds_oe , input bit ram_rwds_in , output bit ram_rwds_out , output bit ram_data_oe , input bit [7:0] ram_data_in , output bit [7:0] ram_data_out ); assign ram_clkn = !ram_clkp; bit valid; ram_command_t command; ram_word_address_t base_address; bit slow; ram_word_count_t word_count; (* syn_encoding = "one-hot" *) enum int unsigned { CHIP_SELECT , SEND_COMMAND_1 , SEND_COMMAND_2 , SEND_COMMAND_3 , SEND_COMMAND_4 , SEND_COMMAND_5 , SEND_COMMAND_6 , LAT2_12 , LAT2_11 , LAT2_10 , LAT2_9 , LAT2_8 , LAT2_7 , LAT2_6 , LAT2_5 , LAT2_4 , LAT2_3 , LAT2_2 , LAT2_1 /* Latency blocks are 6 cycle, but you start counting after the upper * column address is sent (SEND_COMMAND_4) so we reduce the * lowest-latency block by one full cycle (two states). , LAT1_12 , LAT1_11 */ , LAT1_10 , LAT1_9 , LAT1_8 , LAT1_7 , LAT1_6 , LAT1_5 , LAT1_4 , LAT1_3 , LAT1_2 , LAT1_1 , DATA_1 , DATA_2 } state; (* syn_encoding = "compact" *) enum bit { SETUP_OUTPUTS , TOGGLE_CLOCK } half_state; bit [$clog2(`RESET_CYCLES+1):0] reset_counter; bit [$clog2(`TRWR_CYCLES+1):0] trwr_counter; bit prev_rwds; always @(posedge clock) begin if (reset || reset_counter != 0) begin command_ready = 0; result_valid = 0; ram_resetn = 0; ram_csn = 1; ram_clkp = 0; ram_rwds_oe = 0; ram_rwds_out = 0; ram_data_oe = 0; ram_data_out = 0; base_address = 0; slow = 0; word_count = `RAM_LINE_WORDS; trwr_counter = `TRWR_CYCLES; state = state.first; half_state = half_state.first; if (reset) reset_counter = `RESET_CYCLES; // Spec wants >= 100ns of reset else reset_counter = reset_counter - 1; end else begin ram_resetn = 1; if (result_ready) result_valid = 0; if (command_ready && command_valid) begin valid = 1; command = command_data; base_address = 0; base_address[`RAM_ADDRESS_BITS-1:$clog2(`RAM_LINE_WORDS)] = command.address; word_count = `RAM_LINE_WORDS; state = state.first; end if (!valid || trwr_counter != 0) begin ram_rwds_oe = 0; ram_data_oe = 0; ram_csn = 1; ram_clkp = 0; if (trwr_counter != 0) --trwr_counter; end else if (half_state == TOGGLE_CLOCK) begin half_state = half_state.next; if (state != CHIP_SELECT && state != SEND_COMMAND_1) ram_clkp = !ram_clkp; end else if (half_state == SETUP_OUTPUTS) begin automatic bit stall = 0; half_state = half_state.next; ram_rwds_oe = 0; ram_data_oe = 0; case (state) CHIP_SELECT: begin ram_clkp = 0; // Overriding clock to guarantee that we're starting the command with the correct clock polarity ram_csn = 0; end SEND_COMMAND_1: begin ram_data_oe = 1; ram_data_out = {!command.write, 1'b0, 1'b1, 5'b0}; // R/W#, ADDRSPACE, BURST, RESERVED end SEND_COMMAND_2: begin ram_data_oe = 1; ram_data_out = {4'b0, base_address[22:19]}; // RESERVED, ROW end SEND_COMMAND_3: begin ram_data_oe = 1; ram_data_out = {base_address[18:11]}; // ROW end SEND_COMMAND_4: begin ram_data_oe = 1; ram_data_out = {base_address[10:9], base_address[8:3]}; // ROW, UPPERCOL // This is the cycle immediately before the latency countdown *really* begins. // So we capture RWDS now in order to know how many latency blocks are required. slow = ram_rwds_in; end SEND_COMMAND_5: begin ram_data_oe = 1; ram_data_out = {8'b0}; // RESERVED end SEND_COMMAND_6: begin ram_data_oe = 1; ram_data_out = {5'b0, base_address[2:0]}; // RESERVED, LOWERCOL // If we're not in "slow" mode then skip the LAT2 latency // block. Note that the state=state.next will still happen // below, taking us to the first state in the LAT1 block. if (!slow) state = LAT2_1; end // Nothing happens on any of the LAT_* states except for // cycling the clock and advancing state. DATA_1: begin if (command.write) begin ram_rwds_oe = 1; ram_rwds_out = !command.mask[word_count - 1][0]; ram_data_oe = 1; ram_data_out = command.data[word_count - 1][0]; end else if (prev_rwds != ram_rwds_in) begin command.data[word_count - 1][0] = ram_data_in; end else begin stall = 1; end end DATA_2: begin if (command.write) begin ram_rwds_oe = 1; ram_rwds_out = !command.mask[word_count - 1][1]; ram_data_oe = 1; ram_data_out = command.data[word_count - 1][1]; word_count = word_count - 1; if (word_count != 0) begin stall = 1; state = DATA_1; end end else if (prev_rwds != ram_rwds_in) begin command.data[word_count - 1][1] = ram_data_in; word_count = word_count - 1; if (word_count != 0) begin stall = 1; state = DATA_1; end end else begin stall = 1; end end endcase if (!stall) begin state = state.next; if (state == state.first) begin valid = 0; if (!command.write) begin // We know that this is safe because we don't accept commands unless we have result bandwidth result_valid = 1; result_data.address = command.address; result_data.data = command.data; result_data.tag = command.tag; end trwr_counter = `TRWR_CYCLES; end end end prev_rwds = ram_rwds_in; command_ready = !valid && !result_valid; end end endmodule