Hazard3/hdl/arith/hazard5_shift_log_seq.v

107 lines
3.7 KiB
Verilog

/**********************************************************************
* DO WHAT THE FUCK YOU WANT TO AND DON'T BLAME US PUBLIC LICENSE *
* Version 3, April 2008 *
* *
* Copyright (C) 2018 Luke Wren *
* *
* Everyone is permitted to copy and distribute verbatim or modified *
* copies of this license document and accompanying software, and *
* changing either is allowed. *
* *
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION *
* *
* 0. You just DO WHAT THE FUCK YOU WANT TO. *
* 1. We're NOT RESPONSIBLE WHEN IT DOESN'T FUCKING WORK. *
* *
*********************************************************************/
// Implement the three shifts using a single log-sequential shifter.
// On each clock, the shifter can left-shift by a power-of-two amount (arith or
// logical), OR it can reverse the accumulator.
//
// The accumulator is wired in reverse to the output. So the sequences are:
// - Right shift: flip, then shift. Output wiring flips again. Internal left-shifts
// are effectively right shifts.
// - Left shift: perform shift ops, then flip, so that reversed output cancels.
//
// An additional cycle is consumed to load the input into the accumulator; this
// simplifies muxing. In total, a shift consumes between 2 and 7 cycles on a
// 32-bit machine, depending on the bit weight of shamt.
module hazard5_shift_log_seq #(
parameter W_DATA = 32,
parameter W_SHAMT = 5
) (
input wire clk,
input wire rst_n,
input wire [W_DATA-1:0] din,
input wire din_vld, // can be asserted at any time, we always respond
input wire [W_SHAMT-1:0] shamt,
input wire right_nleft,
input wire arith,
output reg [W_DATA-1:0] dout,
output reg dout_vld,
);
reg [W_DATA-1:0] accum;
reg [W_DATA-1:0] accum_next;
reg [W_SHAMT-1:0] shamt_remaining;
reg flipped;
// Handle actual shifting
wire flip = !flipped && (right_nleft || ~|shamt_remaining);
wire sext = arith && accum[0]; // "Left arithmetic" shifting
always @ (*) begin: shift_unit
integer i;
accum_next = accum;
// The following is a priority mux tree (honest) which the synthesis tool should balance
if (din_vld) begin
accum_next = din;
end else if (flip) begin
for (i = 0; i < W_DATA; i = i + 1)
accum_next[i] = accum[W_DATA - 1 - i];
end else if (shamt_remaining) begin
// Smallest shift first
for (i = 0; i < W_SHAMT; i = i + 1) begin
if (shamt_remaining[i] && ~|(shamt_remaining & ~({W_SHAMT{1'b1}} << i))) begin
accum_next = (accum << (1 << i)) |
({W_DATA{sext}} & ~({W_DATA{1'b1}} << (1 << i)));
end
end
end
end
// No reset on datapath
always @ (posedge clk)
accum <= accum_next;
// State machine
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
shamt_remaining <= {W_SHAMT{1'b0}};
flipped <= 1'b0;
end else if (din_vld) begin
shamt_remaining <= shamt;
flipped <= 1'b0;
end else begin
if (flip)
flipped <= 1'b1;
else
shamt_remaining <= shamt_remaining & {shamt_remaining - 1'b1};
end
end
always @ (*) begin: connect_output
dout_vld = flipped && ~|shamt_remaining;
integer i;
for (i = 0; i < W_DATA; i = i + 1)
dout[i] = accum[W_DATA - 1 - i];
end
endmodule