From 95b0e874cf8f99a449a6287484a9121a8f2f348d Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Fri, 17 Nov 2023 21:51:09 -0800 Subject: [PATCH] Implement RTC --- doc/rtc.drawio | 159 ++++++++++++++++++++++ hw/efinix_fpga/simulation/Makefile | 4 +- hw/efinix_fpga/simulation/tbs/rtc_tb.sv | 117 ++++++++++++++++ hw/efinix_fpga/src/rtc.sv | 170 ++++++++++++++++++++++++ 4 files changed, 448 insertions(+), 2 deletions(-) create mode 100644 doc/rtc.drawio create mode 100644 hw/efinix_fpga/simulation/tbs/rtc_tb.sv create mode 100644 hw/efinix_fpga/src/rtc.sv diff --git a/doc/rtc.drawio b/doc/rtc.drawio new file mode 100644 index 0000000..58c1575 --- /dev/null +++ b/doc/rtc.drawio @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw/efinix_fpga/simulation/Makefile b/hw/efinix_fpga/simulation/Makefile index 2d6328d..4782ce3 100644 --- a/hw/efinix_fpga/simulation/Makefile +++ b/hw/efinix_fpga/simulation/Makefile @@ -9,7 +9,7 @@ TEST_PROGRAM_NAME?=loop_test TEST_FOLDER?=$(REPO_TOP)/sw/test_code/$(TEST_PROGRAM_NAME) TEST_PROGRAM?=$(REPO_TOP)/sw/test_code/$(TEST_PROGRAM_NAME)/$(TEST_PROGRAM_NAME).hex -STANDALONE_TB= interrupt_controller_tb mapper_code_tb mapper_tb +STANDALONE_TB= interrupt_controller_tb mapper_code_tb mapper_tb rtc_tb #TODO implement something like sources.list @@ -32,7 +32,7 @@ full_sim: $(TARGET) $(SD_IMAGE) vvp -i $(TARGET) -fst $(STANDALONE_TB): $(SRCS) $(TBS) - iverilog -g2005-sv $(FLAGS) -s $@ -o $@ $(INC) $(SRCS) $(TBS) + iverilog -g2005-sv $(FLAGS) -s $@ -o $@ $(INC) $(SRCS) tbs/$@.sv # mapper_code_tb: $(SRCS) $(TBS) $(INIT_MEM) # iverilog -g2005-sv $(FLAGS) -s $@ -o $@ $(INC) $(SRCS) $(TBS) diff --git a/hw/efinix_fpga/simulation/tbs/rtc_tb.sv b/hw/efinix_fpga/simulation/tbs/rtc_tb.sv new file mode 100644 index 0000000..a70161e --- /dev/null +++ b/hw/efinix_fpga/simulation/tbs/rtc_tb.sv @@ -0,0 +1,117 @@ +`timescale 1ns/1ps + +module rtc_tb(); + +logic r_clk_cpu; + +initial begin + r_clk_cpu <= '1; + forever begin + #125 r_clk_cpu <= ~r_clk_cpu; + end +end + +logic reset, rwb, cs, addr, irq; +logic [7:0] i_data, o_data; + +rtc u_rtc( + .clk(r_clk_cpu), + .reset(reset), + .rwb(rwb), + .cs(cs), + .addr(addr), + .i_data(i_data), + .o_data(o_data), + .irq(irq) +); + +initial begin + do_reset(); + set_increment(1); + set_threshold(7); + set_irq_threshold(2); + enable_rtc(3); + repeat (20) @(posedge r_clk_cpu); + $finish(); +end + +initial begin + $dumpfile("rtc_tb.vcd"); + $dumpvars(0,rtc_tb); +end + +task do_reset(); + repeat (5) @(posedge r_clk_cpu); + reset = 1; + cs = 0; + rwb = 1; + addr = '0; + i_data = '0; + repeat (5) @(posedge r_clk_cpu); + reset = 0; + repeat (5) @(posedge r_clk_cpu); +endtask + +/* These should be shared */ +task write_reg(input logic [4:0] _addr, input logic [7:0] _data); + @(negedge r_clk_cpu); + cs <= '1; + addr <= _addr; + rwb <= '0; + i_data <= '1; + @(posedge r_clk_cpu); + i_data <= _data; + @(negedge r_clk_cpu); + cs <= '0; + rwb <= '1; + @(posedge r_clk_cpu); +endtask + +task read_reg(input logic [2:0] _addr, output logic [7:0] _data); + @(negedge r_clk_cpu); + cs <= '1; + addr <= _addr; + rwb <= '1; + i_data <= '1; + @(posedge r_clk_cpu); + _data <= o_data; + @(negedge r_clk_cpu); + cs <= '0; + rwb <= '1; + @(posedge r_clk_cpu); +endtask + +task set_increment(input logic [31:0] _increment); + for (int i = 0; i < 4; i++) begin + write_reg(0, 8'h10 | i); + write_reg(1, _increment[8*i +: 8]); + end +endtask + +task set_threshold(input logic [31:0] _threshold); + for (int i = 0; i < 4; i++) begin + write_reg(0, 8'h00 | i); + write_reg(1, _threshold[8*i +: 8]); + end +endtask + +task set_irq_threshold(input logic [31:0] _increment); + for (int i = 0; i < 4; i++) begin + write_reg(0, 8'h20 | i); + write_reg(1, _increment[8*i +: 8]); + end +endtask + +task enable_rtc(input logic [7:0] _ctrl); + write_reg(0, 8'h30); + write_reg(1, _ctrl); +endtask + +task read_output(output logic [31:0] _output); + for (int i = 0; i < 4; i++) begin + write_reg(0, 8'h30 | i); + read_reg(1, _output[8*i +: 8]); + end +endtask + +endmodule diff --git a/hw/efinix_fpga/src/rtc.sv b/hw/efinix_fpga/src/rtc.sv new file mode 100644 index 0000000..20554ff --- /dev/null +++ b/hw/efinix_fpga/src/rtc.sv @@ -0,0 +1,170 @@ +module rtc( + input clk, + input reset, + input rwb, + input cs, + input addr, + input [7:0] i_data, + output logic [7:0] o_data, + output logic irq +); + +localparam REG_SIZ = 32; + +logic [REG_SIZ-1:0] r_counter, r_counter_next; +logic [REG_SIZ-1:0] r_irq_counter, r_irq_counter_next; + +// Because we need to increment this, it can't be +// a byte sel register. Thats fine because we don't need +// to be able to write from the cpu anyway. +logic [REG_SIZ-1:0] r_output, r_output_next; + +logic [1:0] w_byte_sel; + +logic w_increment_write; +logic [7:0] w_increment_data; +logic [REG_SIZ-1:0] w_increment_full_data; + +byte_sel_register #( + .DATA_WIDTH(8), + .ADDR_WIDTH(REG_SIZ/8) +) u_increment_reg ( + .i_clk(~clk), + .i_reset(reset), + .i_write(w_increment_write), + .i_byte_sel(w_byte_sel), + .i_data(i_data), + .o_data(w_increment_data), + .o_full_data(w_increment_full_data) +); + +logic w_threshold_write; +logic [7:0] w_threshold_data; +logic [REG_SIZ-1:0] w_threshold_full_data; + +byte_sel_register #( + .DATA_WIDTH(8), + .ADDR_WIDTH(REG_SIZ/8) +) u_threshold_reg ( + .i_clk(~clk), + .i_reset(reset), + .i_write(w_threshold_write), + .i_byte_sel(w_byte_sel), + .i_data(i_data), + .o_data(w_threshold_data), + .o_full_data(w_threshold_full_data) +); + +logic w_irq_threshold_write; +logic [7:0] w_irq_threshold_data; +logic [REG_SIZ-1:0] w_irq_threshold_full_data; + +byte_sel_register #( + .DATA_WIDTH(8), + .ADDR_WIDTH(REG_SIZ/8) +) u_irq_threshold_reg ( + .i_clk(~clk), + .i_reset(reset), + .i_write(w_irq_threshold_write), + .i_byte_sel(w_byte_sel), + .i_data(i_data), + .o_data(w_irq_threshold_data), + .o_full_data(w_irq_threshold_full_data) +); + +logic we, re; + +assign we = cs & ~rwb; +assign re = cs & rwb; + + +logic [7:0] cmd, cmd_next; + +logic [7:0] ctrl, ctrl_next; + +always_comb begin + if (addr == '0 && we) begin + cmd_next = i_data; + end else begin + cmd_next = cmd; + end + + w_increment_write = 0; + w_threshold_write = 0; + w_irq_threshold_write = 0; + w_byte_sel = cmd[3:0]; + + ctrl_next = ctrl; + + if (addr == '1) begin + unique casez (cmd) + 8'h0?: begin + w_threshold_write = we; + o_data = w_threshold_data; + end + + 8'h1?: begin + w_increment_write = we; + o_data = w_increment_data; + end + + 8'h2?: begin + w_irq_threshold_write = we; + o_data = w_irq_threshold_data; + end + + 8'h3?: begin + if (we) begin + ctrl_next = i_data; + end + o_data = r_output[8*w_byte_sel +: 8]; + end + endcase + end +end + +always_comb begin + r_counter_next = r_counter + w_increment_full_data; + + + r_irq_counter_next = r_irq_counter; + r_output_next = r_output; + + if (r_counter == w_threshold_full_data) begin + r_counter_next = '0; + r_irq_counter_next = r_irq_counter + 1; + r_output_next = r_output + 1; + end + + irq = 0; + if (r_irq_counter == w_irq_threshold_full_data) begin + irq = ctrl[1]; + r_irq_counter_next = '0; + end + + if (ctrl[0] == '0) begin + r_irq_counter_next = 0; + r_counter_next = '0; + r_output_next = '0; + end +end + +// Does it matter if we do negedge clock or just invert the input to the module? +always_ff @(negedge clk) begin + if (reset) begin + r_counter <= '0; + r_irq_counter <= '0; + r_output <= '0; + cmd <= '0; + ctrl <= '0; + end else begin + ctrl <= ctrl_next; + cmd <= cmd_next; + r_counter <= r_counter_next; + r_irq_counter <= r_irq_counter_next; + r_output <= r_output_next; + end +end + + +endmodule \ No newline at end of file