aboutsummaryrefslogtreecommitdiffstats
path: root/sources/hdl/verilog/maintenance_controller.v
diff options
context:
space:
mode:
authorAtaberk <olgunataberk@gmail.com>2022-09-25 17:22:03 +0200
committerAtaberk <olgunataberk@gmail.com>2022-09-25 17:22:03 +0200
commitdc0b3db1b4f1895a07e5fe280ee3790e87f97b9f (patch)
treeb47203aa281bdd959def4451c84d310cd9cf2e12 /sources/hdl/verilog/maintenance_controller.v
downloaddram-bender-dc0b3db1b4f1895a07e5fe280ee3790e87f97b9f.tar.gz
Initial commit
Diffstat (limited to 'sources/hdl/verilog/maintenance_controller.v')
-rw-r--r--sources/hdl/verilog/maintenance_controller.v265
1 files changed, 265 insertions, 0 deletions
diff --git a/sources/hdl/verilog/maintenance_controller.v b/sources/hdl/verilog/maintenance_controller.v
new file mode 100644
index 0000000..9354d17
--- /dev/null
+++ b/sources/hdl/verilog/maintenance_controller.v
@@ -0,0 +1,265 @@
+`include "parameters.vh"
+
+module maintenance_controller#(parameter tCK = 1500)(
+
+ input clk,
+ input rst,
+
+ input init_calib_complete,
+ input softmc_fin,
+
+ input aref_en,
+ input aref_en_valid,
+
+ input maint_ack,
+ output maint_req,
+ output per_rd_init,
+ output per_zq_init,
+ output per_ref_init,
+ output maint_process,
+ input program_process,
+
+ input [`IMEM_ADDR_WIDTH-1:0] in_addr,
+ input in_valid,
+
+ output [`INSTR_WIDTH-1:0] out_data,
+ output reg out_valid,
+ output [`IMEM_ADDR_WIDTH-1:0] out_addr
+ );
+
+ wire zq_ack, per_rd_ack, ref_ack;
+ reg zq_request, zq_process;
+ reg per_rd_request, per_rd_process;
+ reg pr_ref_request, pr_ref_process;
+
+ wire [`INSTR_WIDTH-1 : 0] pr_read_out, pr_zq_out, pr_ref_out;
+
+ assign maint_req = per_rd_request | zq_request | pr_ref_request;
+ assign out_addr = {`IMEM_ADDR_WIDTH{`HIGH}};
+ assign out_data = zq_process ? pr_zq_out : per_rd_process ? pr_read_out : pr_ref_out;
+ assign per_ref_init = maint_ack && pr_ref_request && ~per_rd_request && ~zq_request;
+ assign per_rd_init = maint_ack && per_rd_request && ~zq_request;
+ assign per_zq_init = maint_ack && zq_request;
+ assign maint_process = zq_process || per_rd_process || pr_ref_process;
+
+ assign zq_ack = softmc_fin && zq_process;
+ assign per_rd_ack = softmc_fin && per_rd_process;
+ assign ref_ack = softmc_fin && pr_ref_process;
+
+ pr_read_mem prm
+ (
+ .addra(in_addr[3:0]),
+ .clka(clk),
+ .douta(pr_read_out),
+ .dina(64'bX),
+ .wea(`LOW),
+ .ena(in_valid && per_rd_process)
+ );
+
+ zq_calib_mem pzm
+ (
+ .addra(in_addr[5:0]),
+ .clka(clk),
+ .douta(pr_zq_out),
+ .dina(64'bX),
+ .wea(`LOW),
+ .ena(in_valid && zq_process)
+ );
+
+ pr_ref_mem prefm
+ (
+ .addra(in_addr[6:0]),
+ .clka(clk),
+ .douta(pr_ref_out),
+ .dina(64'bX),
+ .wea(`LOW),
+ .ena(in_valid && pr_ref_process)
+ );
+
+ always @(posedge clk) begin
+ if(rst) begin
+ zq_process <= `LOW;
+ per_rd_process <= `LOW;
+ out_valid <= `LOW;
+ pr_ref_process <= `LOW;
+ end
+ else begin
+ if(softmc_fin) begin
+ if(zq_process)
+ zq_process <= `LOW;
+ if(per_rd_process)
+ per_rd_process <= `LOW;
+ if(pr_ref_process)
+ pr_ref_process <= `LOW;
+ out_valid <= `LOW;
+ end
+ else if(maint_ack) begin
+ if(zq_request)
+ zq_process <= `HIGH;
+ else if(per_rd_request)
+ per_rd_process <= `HIGH;
+ else if(pr_ref_request)
+ pr_ref_process <= `HIGH;
+ out_valid <= `LOW;
+ end
+ else begin
+ zq_process <= zq_process;
+ per_rd_process <= per_rd_process;
+ pr_ref_process <= pr_ref_process;
+ if(maint_process)
+ out_valid <= in_valid;
+ else
+ out_valid <= `LOW;
+ end
+ end
+ end
+
+ // Maintenance control logic
+ // Set request bits according to timers
+
+ localparam MAINT_PRESCALER_PERIOD = 500_000; // .5 us
+
+ function integer clogb2 (input integer size); // ceiling logb2
+ begin
+ size = size - 1;
+ for (clogb2=1; size>1; clogb2=clogb2+1)
+ size = size >> 1;
+ end
+ endfunction // clogb2
+
+ localparam MAINT_PRESCALER_DIV = MAINT_PRESCALER_PERIOD/(tCK * 4); // softmc is clocked 4 times slower than the memory interface
+ localparam MAINT_PRESCALER_WIDTH = clogb2(MAINT_PRESCALER_DIV + 1);
+ localparam ONE = 1;
+
+ reg maint_prescaler_tick_r_lcl;
+ reg [MAINT_PRESCALER_WIDTH-1:0] maint_prescaler_r;
+ reg [MAINT_PRESCALER_WIDTH-1:0] maint_prescaler_ns;
+
+ wire maint_prescaler_tick_ns = (maint_prescaler_r == ONE[MAINT_PRESCALER_WIDTH-1:0]);
+
+ always @(/*AS*/init_calib_complete or maint_prescaler_r
+ or maint_prescaler_tick_ns) begin
+ maint_prescaler_ns = maint_prescaler_r;
+ if (~init_calib_complete || maint_prescaler_tick_ns)
+ maint_prescaler_ns = MAINT_PRESCALER_DIV[MAINT_PRESCALER_WIDTH-1:0];
+ else if (|maint_prescaler_r)
+ maint_prescaler_ns = maint_prescaler_r - ONE[MAINT_PRESCALER_WIDTH-1:0];
+ end
+
+ always @(posedge clk) maint_prescaler_r <= maint_prescaler_ns;
+
+ always @(posedge clk) maint_prescaler_tick_r_lcl <= maint_prescaler_tick_ns;
+
+ localparam tPRDI = 1_000_000;
+ localparam PERIODIC_RD_TIMER_DIV = tPRDI/MAINT_PRESCALER_PERIOD;
+ localparam PERIODIC_RD_TIMER_WIDTH = clogb2(PERIODIC_RD_TIMER_DIV + /*idle state*/ 1);
+
+ reg [PERIODIC_RD_TIMER_WIDTH-1:0] periodic_rd_timer_r, periodic_rd_timer;
+
+ always @* begin
+ periodic_rd_timer = periodic_rd_timer_r;
+
+ if(~init_calib_complete) begin
+ periodic_rd_timer = {PERIODIC_RD_TIMER_WIDTH{1'b0}};
+ end
+ else if (per_rd_ack || program_process) begin
+ periodic_rd_timer = PERIODIC_RD_TIMER_DIV[0+:PERIODIC_RD_TIMER_WIDTH];
+ end
+ else if (|periodic_rd_timer_r && maint_prescaler_tick_r_lcl) begin
+ periodic_rd_timer = periodic_rd_timer_r - ONE[0+:PERIODIC_RD_TIMER_WIDTH];
+ end
+ end //always
+
+ wire periodic_rd_timer_one = maint_prescaler_tick_r_lcl && (periodic_rd_timer_r == ONE[0+:PERIODIC_RD_TIMER_WIDTH]);
+
+ wire periodic_rd_request = ~rst && (/*((PERIODIC_RD_TIMER_DIV != 0) && ~dfi_init_complete) ||*/
+ (~per_rd_ack && (per_rd_request || periodic_rd_timer_one)));
+
+ always @(posedge clk) begin
+ if(~init_calib_complete)
+ periodic_rd_timer_r <= PERIODIC_RD_TIMER_DIV[0+:PERIODIC_RD_TIMER_WIDTH];
+ else
+ periodic_rd_timer_r <= periodic_rd_timer;
+ per_rd_request <= periodic_rd_request;
+ end //always
+
+ // ZQ timebase. Nominally 128 mS
+ localparam MAINT_PRESCALER_PERIOD_NS = MAINT_PRESCALER_PERIOD / 1000;
+ localparam tZQI = 128_000_000;
+ localparam ZQ_TIMER_DIV = tZQI/MAINT_PRESCALER_PERIOD_NS;
+ localparam ZQ_TIMER_WIDTH = clogb2(ZQ_TIMER_DIV + 1);
+
+ generate
+ begin : zq_cntrl
+ reg zq_tick = 1'b0;
+
+ if (ZQ_TIMER_DIV !=0) begin : zq_timer
+ reg [ZQ_TIMER_WIDTH-1:0] zq_timer_r;
+ reg [ZQ_TIMER_WIDTH-1:0] zq_timer_ns;
+
+ always @(/*AS*/init_calib_complete or maint_prescaler_tick_r_lcl or zq_tick or zq_timer_r or program_process) begin
+ zq_timer_ns = zq_timer_r;
+ if (~init_calib_complete || zq_tick || program_process)
+ zq_timer_ns = ZQ_TIMER_DIV[ZQ_TIMER_WIDTH-1:0];
+ else if (|zq_timer_r && maint_prescaler_tick_r_lcl)
+ zq_timer_ns = zq_timer_r - ONE[ZQ_TIMER_WIDTH-1:0];
+ end
+
+ always @(posedge clk) zq_timer_r <= zq_timer_ns;
+
+ always @(/*AS*/maint_prescaler_tick_r_lcl or zq_timer_r)
+ zq_tick = (zq_timer_r == ONE[ZQ_TIMER_WIDTH-1:0] && maint_prescaler_tick_r_lcl);
+ end // zq_timer
+
+ // ZQ request. Set request with timer tick, and when exiting PHY init. Never
+ // request if ZQ_TIMER_DIV == 0.
+ begin : zq_request_logic
+ wire zq_clear = zq_ack;
+ reg zq_request_r;
+ wire zq_request_ns = ~rst && ((~init_calib_complete && (ZQ_TIMER_DIV != 0)) ||
+ (zq_request_r && ~zq_clear) || zq_tick);
+
+ always @(posedge clk) zq_request_r <= zq_request_ns;
+
+ always @(/*AS*/init_calib_complete or zq_request_r)
+ zq_request = init_calib_complete && zq_request_r;
+ end // zq_request_logic
+ end
+ endgenerate
+
+ localparam tAREF = 7_800;
+ localparam PERIODIC_REF_TIMER_DIV = tAREF/MAINT_PRESCALER_PERIOD_NS;
+ localparam PERIODIC_REF_TIMER_WIDTH = clogb2(PERIODIC_REF_TIMER_DIV + /*idle state*/ 1);
+
+ reg [PERIODIC_REF_TIMER_WIDTH-1:0] autoref_timer_r, autoref_timer;
+ reg aref_switch_ns, aref_switch_r;
+
+ always @* begin
+ aref_switch_ns = aref_en_valid ? aref_en : aref_switch_r;
+ pr_ref_request = `LOW;
+ autoref_timer = autoref_timer_r;
+ if(~aref_switch_r || ~init_calib_complete)
+ autoref_timer = PERIODIC_REF_TIMER_DIV[PERIODIC_REF_TIMER_WIDTH-1:0];
+ else begin
+ if(autoref_timer_r > 1 && maint_prescaler_tick_r_lcl) // you were here
+ autoref_timer = autoref_timer_r - ONE[PERIODIC_REF_TIMER_WIDTH-1:0];
+ else if(autoref_timer_r == 1) begin
+ pr_ref_request = `HIGH;
+ if(ref_ack)
+ autoref_timer = PERIODIC_REF_TIMER_DIV[PERIODIC_REF_TIMER_WIDTH-1:0];
+ end
+ end
+ end
+
+ always @(posedge clk) begin
+ if(rst) begin
+ aref_switch_r <= `LOW;
+ autoref_timer_r = PERIODIC_REF_TIMER_DIV[PERIODIC_REF_TIMER_WIDTH-1:0];
+ end
+ else begin
+ aref_switch_r <= aref_switch_ns;
+ autoref_timer_r <= autoref_timer;
+ end
+ end
+
+endmodule