107 lines
3.7 KiB
Verilog
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
|