Behavioral Verilog is the most abstract style of Verilog code. The code looks very similar to C, but one has to remember that in hardware operations occur in parallel, and everything is running at once.
This sample register file will be a 16 entries by 8-bits per entry. It will have 2 synchronous write ports, and an asynchronous read port as well as an active low asynchronous reset.
We begin with our module header, parameters, and i/o declarations.
module register_file(data_out, clk, wr_en_1, wr_en_2, rst, data_in_1, data_in_2, wr_addr_1, wr_addr_2, rd_addr); parameter ENTRIES = 16; parameter REG_OFFSET = 8; integer i; input clk, wr_en_1, wr_en_2, rst; input [7:0] data_in_1, data_in_2; input [2:0] wr_addr_1, wr_addr_2; input [3:0] rd_addr; output reg [7:0] data_out; reg [7:0] registers [ENTRIES - 1:0];
By using parameters we can easily create register files of different sizes by passing new parameter values during instantiation. This can be done as such:
register_file #(16, 32) reg_file(data_out, clk, wr_en_1, wr_en_2, rst, data_in_1, data_in_2, wr_addr_1, wr_addr_2, rd_addr);
This will instantiate the same module with 16 32-bit entries.
Inside our module in an always block we declare that we want it to execute only in case of a clock’s positive edge, or a reset’s negative edge. Since the rest is active low, it will change states from 1->0 (negative edge).
always @(posedge clk, negedge rst) begin
We then take care of our two write ports and reset. If reset is equal to 0, then we need to reset all of the values in the register file. Otherwise if wr_en_1 is 1, we want to write to write port 1, else we want to write to write port 2.
always @(posedge clk, negedge rst) begin if(rst == 1'b0) begin for(i = 0; i < ENTRIES; i = i + 1) begin registers[i] <= 7'b0; end end else if(wr_en_1) registers[wr_addr_1] <= data_in_1; else registers[REG_OFFSET + wr_addr_2] <= data_in_2; end
That take care of the two synchronous write ports. Since hardware operations occur in parallel, we can have multiple always blocs which will all execute at the same time. For the write port we simply want the always block to be triggered every time the read address (read_addr) changes.
always @(rd_addr) begin data_out = registers[rd_addr]; end
And this is the complete code for the register file:
register_file.v:
module register_file(data_out, clk, wr_en_1, wr_en_2, rst, data_in_1, data_in_2, wr_addr_1, wr_addr_2, rd_addr); parameter ENTRIES = 16; parameter REG_OFFSET = 8; integer i; input clk, wr_en_1, wr_en_2, rst; input [7:0] data_in_1, data_in_2; input [2:0] wr_addr_1, wr_addr_2; input [3:0] rd_addr; output reg [7:0] data_out; reg [7:0] registers [ENTRIES - 1:0]; always @(posedge clk, negedge rst) begin if(rst == 1'b0) begin for(i = 0; i < ENTRIES; i = i + 1) begin registers[i] <= 7'b0; end end else if(wr_en_1) registers[wr_addr_1] <= data_in_1; else registers[REG_OFFSET + wr_addr_2] <= data_in_2; end always @(rd_addr) begin data_out = registers[rd_addr]; end endmodule
