From 0553c4839c06011bd044f69b4913e5c793fdd2ec Mon Sep 17 00:00:00 2001 From: Julian Blake Kongslie Date: Sun, 27 Feb 2022 17:21:05 -0800 Subject: Initial commit. --- hdl/ram_controller.sv | 240 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 hdl/ram_controller.sv (limited to 'hdl/ram_controller.sv') diff --git a/hdl/ram_controller.sv b/hdl/ram_controller.sv new file mode 100644 index 0000000..6eeb46d --- /dev/null +++ b/hdl/ram_controller.sv @@ -0,0 +1,240 @@ +`include "defs.svh" + +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 [2:0] reset_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; + state = state.first; + half_state = half_state.first; + if (reset) + reset_counter = 5; // 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) begin + ram_rwds_oe = 0; + ram_data_oe = 0; + ram_csn = 1; + ram_clkp = 0; + 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 + end + end + end + + prev_rwds = ram_rwds_in; + command_ready = !valid && !result_valid; + end + end + +endmodule -- cgit v1.2.3