mirror of
https://github.com/olofk/serv.git
synced 2026-01-11 23:42:50 +00:00
106 lines
3.4 KiB
Verilog
106 lines
3.4 KiB
Verilog
/*
|
|
* serv_bufreg2.v : SERV buffer register for load/store data and shift amount
|
|
*
|
|
* SPDX-FileCopyrightText: 2022 Olof Kindgren <olof@award-winning.me>
|
|
* SPDX-License-Identifier: ISC
|
|
*/
|
|
module serv_bufreg2
|
|
#(parameter W = 1,
|
|
//Internally calculated. Do not touch
|
|
parameter B=W-1)
|
|
(
|
|
input wire i_clk,
|
|
//State
|
|
input wire i_en,
|
|
input wire i_init,
|
|
input wire i_cnt7,
|
|
input wire i_cnt_done,
|
|
input wire i_sh_right,
|
|
input wire [1:0] i_lsb,
|
|
input wire [1:0] i_bytecnt,
|
|
output wire o_sh_done,
|
|
//Control
|
|
input wire i_op_b_sel,
|
|
input wire i_shift_op,
|
|
//Data
|
|
input wire [B:0] i_rs2,
|
|
input wire [B:0] i_imm,
|
|
output wire [B:0] o_op_b,
|
|
output wire [B:0] o_q,
|
|
//External
|
|
output wire [31:0] o_dat,
|
|
input wire i_load,
|
|
input wire [31:0] i_dat);
|
|
|
|
// High and low data words form a 32-bit word
|
|
reg [7:0] dhi;
|
|
reg [23:0] dlo;
|
|
|
|
/*
|
|
Before a store operation, the data to be written needs to be shifted into
|
|
place. Depending on the address alignment, we need to shift different
|
|
amounts. One formula for calculating this is to say that we shift when
|
|
i_lsb + i_bytecnt < 4. Unfortunately, the synthesis tools don't seem to be
|
|
clever enough so the hideous expression below is used to achieve the same
|
|
thing in a more optimal way.
|
|
*/
|
|
wire byte_valid
|
|
= (!i_lsb[0] & !i_lsb[1]) |
|
|
(!i_bytecnt[0] & !i_bytecnt[1]) |
|
|
(!i_bytecnt[1] & !i_lsb[1]) |
|
|
(!i_bytecnt[1] & !i_lsb[0]) |
|
|
(!i_bytecnt[0] & !i_lsb[1]);
|
|
|
|
assign o_op_b = i_op_b_sel ? i_rs2 : i_imm;
|
|
|
|
wire shift_en = i_shift_op ? (i_en & i_init & (i_bytecnt == 2'b00)) : (i_en & byte_valid);
|
|
|
|
wire cnt_en = (i_shift_op & (!i_init | (i_cnt_done & i_sh_right)));
|
|
|
|
/* The dat register has three different use cases for store, load and
|
|
shift operations.
|
|
store : Data to be written is shifted to the correct position in dat during
|
|
init by shift_en and is presented on the data bus as o_wb_dat
|
|
load : Data from the bus gets latched into dat during i_wb_ack and is then
|
|
shifted out at the appropriate time to end up in the correct
|
|
position in rd
|
|
shift : Data is shifted in during init. After that, the six LSB are used as
|
|
a downcounter (with bit 5 initially set to 0) that trigger
|
|
o_sh_done when they wrap around to indicate that
|
|
the requested number of shifts have been performed
|
|
*/
|
|
|
|
wire [7:0] cnt_next;
|
|
generate
|
|
if (W == 1) begin : gen_cnt_w_eq_1
|
|
assign cnt_next = {o_op_b, dhi[7], dhi[5:0]-6'd1};
|
|
end else if (W == 4) begin : gen_cnt_w_eq_4
|
|
assign cnt_next = {o_op_b[3:2], dhi[5:0]-6'd4};
|
|
end
|
|
endgenerate
|
|
|
|
wire [7:0] dat_shamt = cnt_en ?
|
|
//Down counter mode
|
|
cnt_next :
|
|
//Shift reg mode
|
|
{o_op_b, dhi[7:W]};
|
|
|
|
assign o_sh_done = dat_shamt[5];
|
|
|
|
assign o_q =
|
|
({W{(i_lsb == 2'd3)}} & o_dat[W+23:24]) |
|
|
({W{(i_lsb == 2'd2)}} & o_dat[W+15:16]) |
|
|
({W{(i_lsb == 2'd1)}} & o_dat[W+7:8]) |
|
|
({W{(i_lsb == 2'd0)}} & o_dat[W-1:0]);
|
|
|
|
assign o_dat = {dhi,dlo};
|
|
|
|
always @(posedge i_clk) begin
|
|
if (shift_en | cnt_en | i_load)
|
|
dhi <= i_load ? i_dat[31:24] : dat_shamt & {2'b11, !(i_shift_op & i_cnt7 & !cnt_en), 5'b11111};
|
|
if (shift_en | i_load)
|
|
dlo <= i_load ? i_dat[23:0] : {dhi[B:0], dlo[23:W]};
|
|
end
|
|
|
|
endmodule
|