mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-19 03:44:46 -04:00
365 lines
14 KiB
Systemverilog
365 lines
14 KiB
Systemverilog
// Copyright 2018 - 2019 ETH Zurich and University of Bologna.
|
|
// Copyright and related rights are licensed under the Solderpad Hardware
|
|
// License, Version 0.51 (the "License"); you may not use this file except in
|
|
// compliance with the License. You may obtain a copy of the License at
|
|
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
|
|
// or agreed to in writing, software, hardware and materials distributed under
|
|
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations under the License.
|
|
//
|
|
// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
|
|
// Description: Instruction Re-aligner
|
|
//
|
|
// This module takes cache blocks and extracts the instructions.
|
|
// As we are supporting the compressed instruction set extension, in a 32 bit instruction word
|
|
// are up to 2 compressed instructions.
|
|
// Furthermore those instructions can be arbitrarily interleaved which makes it possible to fetch
|
|
// only the lower part of a 32 bit instruction.
|
|
// Furthermore we need to handle the case if we want to start fetching from an unaligned
|
|
// instruction e.g. a branch.
|
|
|
|
module instr_realign
|
|
import ariane_pkg::*;
|
|
#(
|
|
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty
|
|
) (
|
|
// Subsystem Clock - SUBSYSTEM
|
|
input logic clk_i,
|
|
// Asynchronous reset active low - SUBSYSTEM
|
|
input logic rst_ni,
|
|
// Fetch flush request - CONTROLLER
|
|
input logic flush_i,
|
|
// 32-bit block is valid - CACHE
|
|
input logic valid_i,
|
|
// Instruction is unaligned - FRONTEND
|
|
output logic serving_unaligned_o,
|
|
// 32-bit block address - CACHE
|
|
input logic [CVA6Cfg.VLEN-1:0] address_i,
|
|
// 32-bit block - CACHE
|
|
input logic [CVA6Cfg.FETCH_WIDTH-1:0] data_i,
|
|
// instruction is valid - FRONTEND
|
|
output logic [CVA6Cfg.INSTR_PER_FETCH-1:0] valid_o,
|
|
// Instruction address - FRONTEND
|
|
output logic [CVA6Cfg.INSTR_PER_FETCH-1:0][CVA6Cfg.VLEN-1:0] addr_o,
|
|
// Instruction - instr_scan&instr_queue
|
|
output logic [CVA6Cfg.INSTR_PER_FETCH-1:0][31:0] instr_o
|
|
);
|
|
// as a maximum we support a fetch width of 64-bit, hence there can be 4 compressed instructions
|
|
logic [CVA6Cfg.INSTR_PER_FETCH-1:0] instr_is_compressed;
|
|
|
|
for (genvar i = 0; i < CVA6Cfg.INSTR_PER_FETCH; i++) begin
|
|
// LSB != 2'b11
|
|
assign instr_is_compressed[i] = ~&data_i[i*16+:2];
|
|
end
|
|
|
|
// save the unaligned part of the instruction to this ff
|
|
logic [15:0] unaligned_instr_d, unaligned_instr_q;
|
|
// the last instruction was unaligned
|
|
logic unaligned_d, unaligned_q;
|
|
// register to save the unaligned address
|
|
logic [CVA6Cfg.VLEN-1:0] unaligned_address_d, unaligned_address_q;
|
|
// we have an unaligned instruction
|
|
assign serving_unaligned_o = unaligned_q;
|
|
|
|
// Instruction re-alignment
|
|
if (CVA6Cfg.FETCH_WIDTH == 32) begin : realign_bp_32
|
|
always_comb begin : re_align
|
|
unaligned_d = unaligned_q;
|
|
unaligned_address_d = {address_i[CVA6Cfg.VLEN-1:2], 2'b10};
|
|
unaligned_instr_d = data_i[31:16];
|
|
|
|
valid_o[0] = valid_i;
|
|
instr_o[0] = unaligned_q ? {data_i[15:0], unaligned_instr_q} : data_i[31:0];
|
|
addr_o[0] = unaligned_q ? unaligned_address_q : address_i;
|
|
|
|
if (CVA6Cfg.INSTR_PER_FETCH != 1) begin
|
|
valid_o[CVA6Cfg.INSTR_PER_FETCH-1] = 1'b0;
|
|
instr_o[CVA6Cfg.INSTR_PER_FETCH-1] = '0;
|
|
addr_o[CVA6Cfg.INSTR_PER_FETCH-1] = {address_i[CVA6Cfg.VLEN-1:2], 2'b10};
|
|
end
|
|
// this instruction is compressed or the last instruction was unaligned
|
|
if (instr_is_compressed[0] || unaligned_q) begin
|
|
// check if this is instruction is still unaligned e.g.: it is not compressed
|
|
// if its compressed re-set unaligned flag
|
|
// for 32 bit we can simply check the next instruction and whether it is compressed or not
|
|
// if it is compressed the next fetch will contain an aligned instruction
|
|
// is instruction 1 also compressed
|
|
// yes? -> no problem, no -> we've got an unaligned instruction
|
|
if (instr_is_compressed[CVA6Cfg.INSTR_PER_FETCH-1] && CVA6Cfg.RVC) begin
|
|
unaligned_d = 1'b0;
|
|
valid_o[CVA6Cfg.INSTR_PER_FETCH-1] = valid_i;
|
|
instr_o[CVA6Cfg.INSTR_PER_FETCH-1] = {16'b0, data_i[31:16]};
|
|
end else begin
|
|
// save the upper bits for next cycle
|
|
unaligned_d = 1'b1;
|
|
unaligned_instr_d = data_i[31:16];
|
|
unaligned_address_d = {address_i[CVA6Cfg.VLEN-1:2], 2'b10};
|
|
end
|
|
end // else -> normal fetch
|
|
|
|
// we started to fetch on a unaligned boundary with a whole instruction -> wait until we've
|
|
// received the next instruction
|
|
if (valid_i && address_i[1]) begin
|
|
// the instruction is not compressed so we can't do anything in this cycle
|
|
if (!instr_is_compressed[0]) begin
|
|
valid_o = '0;
|
|
unaligned_d = 1'b1;
|
|
unaligned_address_d = {address_i[CVA6Cfg.VLEN-1:2], 2'b10};
|
|
unaligned_instr_d = data_i[15:0];
|
|
// the instruction isn't compressed but only the lower is ready
|
|
end else begin
|
|
valid_o = {{CVA6Cfg.INSTR_PER_FETCH - 1{1'b0}}, 1'b1};
|
|
end
|
|
end
|
|
end
|
|
end else if (CVA6Cfg.FETCH_WIDTH == 64) begin : realign_bp_64
|
|
always_comb begin : re_align
|
|
unaligned_d = 1'b0;
|
|
unaligned_address_d = unaligned_address_q;
|
|
unaligned_instr_d = unaligned_instr_q;
|
|
|
|
valid_o = '0;
|
|
instr_o[0] = '0;
|
|
addr_o[0] = '0;
|
|
instr_o[1] = '0;
|
|
addr_o[1] = '0;
|
|
instr_o[2] = '0;
|
|
addr_o[2] = '0;
|
|
instr_o[3] = {16'b0, data_i[63:48]};
|
|
addr_o[3] = {address_i[CVA6Cfg.VLEN-1:3], 3'b110};
|
|
|
|
case (address_i[2:1])
|
|
2'b00: begin
|
|
valid_o[0] = valid_i;
|
|
valid_o[1] = valid_i;
|
|
|
|
unaligned_d = unaligned_q;
|
|
|
|
// last instruction was unaligned
|
|
// TODO how are jumps + unaligned managed?
|
|
if (unaligned_q) begin
|
|
// for 64 bit there exist the following options:
|
|
// 64 48 32 16 0
|
|
// | 3 | 2 | 1 | 0 | <- instruction slot
|
|
// | I | I | U | -> again unaligned
|
|
// | * | C | I | U | -> aligned
|
|
// | * | I | C | U | -> aligned
|
|
// | I | C | C | U | -> again unaligned
|
|
// | * | C | C | C | U | -> aligned
|
|
// Legend: C = compressed, I = 32 bit instruction, U = unaligned upper half
|
|
|
|
instr_o[0] = {data_i[15:0], unaligned_instr_q};
|
|
addr_o[0] = unaligned_address_q;
|
|
|
|
instr_o[1] = data_i[47:16];
|
|
addr_o[1] = {address_i[CVA6Cfg.VLEN-1:3], 3'b010};
|
|
|
|
if (instr_is_compressed[1]) begin
|
|
instr_o[2] = data_i[63:32];
|
|
addr_o[2] = {address_i[CVA6Cfg.VLEN-1:3], 3'b100};
|
|
valid_o[2] = valid_i;
|
|
|
|
if (instr_is_compressed[2]) begin
|
|
if (instr_is_compressed[3]) begin
|
|
unaligned_d = 1'b0;
|
|
valid_o[3] = valid_i;
|
|
end else begin
|
|
unaligned_instr_d = instr_o[3];
|
|
unaligned_address_d = addr_o[3];
|
|
end
|
|
end else begin
|
|
unaligned_d = 1'b0;
|
|
valid_o[2] = valid_i;
|
|
end
|
|
end else begin
|
|
instr_o[2] = instr_o[3];
|
|
addr_o[2] = addr_o[3];
|
|
if (instr_is_compressed[3]) begin
|
|
unaligned_d = 1'b0;
|
|
valid_o[2] = valid_i;
|
|
end else begin
|
|
unaligned_instr_d = instr_o[3];
|
|
unaligned_address_d = addr_o[3];
|
|
end
|
|
end
|
|
end else begin
|
|
instr_o[0] = data_i[31:0];
|
|
addr_o[0] = address_i;
|
|
|
|
if (instr_is_compressed[0]) begin
|
|
instr_o[1] = data_i[47:16];
|
|
addr_o[1] = {address_i[CVA6Cfg.VLEN-1:3], 3'b010};
|
|
|
|
// 64 48 32 16 0
|
|
// | 3 | 2 | 1 | 0 | <- instruction slot
|
|
// | I | I | C | -> again unaligned
|
|
// | * | C | I | C | -> aligned
|
|
// | * | I | C | C | -> aligned
|
|
// | I | C | C | C | -> again unaligned
|
|
// | * | C | C | C | C | -> aligned
|
|
if (instr_is_compressed[1]) begin
|
|
instr_o[2] = data_i[63:32];
|
|
addr_o[2] = {address_i[CVA6Cfg.VLEN-1:3], 3'b100};
|
|
valid_o[2] = valid_i;
|
|
|
|
if (instr_is_compressed[2]) begin
|
|
if (instr_is_compressed[3]) begin
|
|
valid_o[3] = valid_i;
|
|
end else begin
|
|
unaligned_d = 1'b1;
|
|
unaligned_instr_d = instr_o[3];
|
|
unaligned_address_d = addr_o[3];
|
|
end
|
|
end
|
|
end else begin
|
|
instr_o[2] = instr_o[3];
|
|
addr_o[2] = addr_o[3];
|
|
|
|
if (instr_is_compressed[3]) begin
|
|
valid_o[2] = valid_i;
|
|
end else begin
|
|
unaligned_d = 1'b1;
|
|
unaligned_instr_d = instr_o[3];
|
|
unaligned_address_d = addr_o[3];
|
|
end
|
|
end
|
|
end else begin
|
|
// 64 32 0
|
|
// | 3 | 2 | 1 | 0 | <- instruction slot
|
|
// | I | C | I |
|
|
// | * | C | C | I |
|
|
// | * | I | I |
|
|
instr_o[1] = data_i[63:32];
|
|
addr_o[1] = {address_i[CVA6Cfg.VLEN-1:3], 3'b100};
|
|
|
|
instr_o[2] = instr_o[3];
|
|
addr_o[2] = addr_o[3];
|
|
|
|
if (instr_is_compressed[2]) begin
|
|
if (instr_is_compressed[3]) begin
|
|
valid_o[2] = valid_i;
|
|
end else begin
|
|
unaligned_d = 1'b1;
|
|
unaligned_instr_d = instr_o[3];
|
|
unaligned_address_d = addr_o[3];
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
// this means the previous instruction was either compressed or unaligned
|
|
// in any case we don't care
|
|
// TODO input is actually right-shifted so the code below is wrong
|
|
2'b01: begin
|
|
// 64 48 32 16 0
|
|
// | 3 | 2 | 1 | 0 | <- instruction slot
|
|
// | I | I | -> again unaligned
|
|
// | * | C | I | -> aligned
|
|
// | * | I | C | -> aligned
|
|
// | I | C | C | -> again unaligned
|
|
// | * | C | C | C | -> aligned
|
|
// 000 110 100 010 <- unaligned address
|
|
|
|
instr_o[0] = data_i[31:0];
|
|
addr_o[0] = {address_i[CVA6Cfg.VLEN-1:3], 3'b010};
|
|
valid_o[0] = valid_i;
|
|
|
|
instr_o[2] = data_i[63:32];
|
|
addr_o[2] = {address_i[CVA6Cfg.VLEN-1:3], 3'b110};
|
|
|
|
if (instr_is_compressed[0]) begin
|
|
instr_o[1] = data_i[47:16];
|
|
addr_o[1] = {address_i[CVA6Cfg.VLEN-1:3], 3'b100};
|
|
valid_o[1] = valid_i;
|
|
|
|
if (instr_is_compressed[1]) begin
|
|
if (instr_is_compressed[2]) begin
|
|
valid_o[2] = valid_i;
|
|
end else begin
|
|
unaligned_d = 1'b1;
|
|
unaligned_instr_d = instr_o[2];
|
|
unaligned_address_d = addr_o[2];
|
|
end
|
|
end
|
|
end else begin
|
|
instr_o[1] = instr_o[2];
|
|
addr_o[1] = addr_o[2];
|
|
|
|
if (instr_is_compressed[2]) begin
|
|
valid_o[1] = valid_i;
|
|
end else begin
|
|
unaligned_d = 1'b1;
|
|
unaligned_instr_d = instr_o[2];
|
|
unaligned_address_d = addr_o[2];
|
|
end
|
|
end
|
|
end
|
|
2'b10: begin
|
|
// 64 48 32 16 0
|
|
// | 3 | 2 | 1 | 0 | <- instruction slot
|
|
// | * | I | C | <- unaligned
|
|
// | * | C | C | <- aligned
|
|
// | * | I | <- aligned
|
|
// 1000 110 100 <- unaligned address
|
|
|
|
instr_o[0] = data_i[31:0];
|
|
addr_o[0] = {address_i[CVA6Cfg.VLEN-1:3], 3'b100};
|
|
valid_o[0] = valid_i;
|
|
|
|
instr_o[1] = data_i[47:16];
|
|
addr_o[1] = {address_i[CVA6Cfg.VLEN-1:3], 3'b110};
|
|
|
|
if (instr_is_compressed[0]) begin
|
|
if (instr_is_compressed[1]) begin
|
|
valid_o[1] = valid_i;
|
|
end else begin
|
|
unaligned_d = 1'b1;
|
|
unaligned_instr_d = instr_o[1];
|
|
unaligned_address_d = addr_o[1];
|
|
end
|
|
end
|
|
end
|
|
// we started to fetch on a unaligned boundary with a whole instruction -> wait until we've
|
|
// received the next instruction
|
|
2'b11: begin
|
|
// 64 48 32 16 0
|
|
// | 3 | 2 | 1 | 0 | <- instruction slot
|
|
// | * | I | <- unaligned
|
|
// | * | C | <- aligned
|
|
// 1000 110 <- unaligned address
|
|
|
|
instr_o[0] = data_i[31:0];
|
|
addr_o[0] = {address_i[CVA6Cfg.VLEN-1:3], 3'b110};
|
|
|
|
if (instr_is_compressed[0]) begin
|
|
valid_o[0] = valid_i;
|
|
end else begin
|
|
unaligned_d = 1'b1;
|
|
unaligned_instr_d = instr_o[0];
|
|
unaligned_address_d = addr_o[0];
|
|
end
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (~rst_ni) begin
|
|
unaligned_q <= 1'b0;
|
|
unaligned_address_q <= '0;
|
|
unaligned_instr_q <= '0;
|
|
end else begin
|
|
if (valid_i) begin
|
|
unaligned_address_q <= unaligned_address_d;
|
|
unaligned_instr_q <= unaligned_instr_d;
|
|
end
|
|
|
|
if (flush_i) begin
|
|
unaligned_q <= 1'b0;
|
|
end else if (valid_i) begin
|
|
unaligned_q <= unaligned_d;
|
|
end
|
|
end
|
|
end
|
|
endmodule
|