blob: d00d52778c1fe9b36b1377248bc18ceb8351b90b [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
`include "prim_assert.sv"
/**
* OTBN Load-Store Unit
*
* Read and write data from/to the data memory (DMEM). Used by the base and the BN instruction
* subset; loads and stores are hence either 32b or WLEN bit wide.
*
* The data memory interface makes the following assumptions:
* - All requests are answered in the next cycle; the LSU must have exclusive access to the memory.
* - The write mask supports aligned 32b write accesses.
*/
module otbn_lsu
import otbn_pkg::*;
#(
parameter int DmemSizeByte = 4096,
localparam int DmemAddrWidth = prim_util_pkg::vbits(DmemSizeByte)
) (
input logic clk_i,
input logic rst_ni,
// Data memory (DMEM) interface
output logic dmem_req_o,
output logic dmem_write_o,
output logic [DmemAddrWidth-1:0] dmem_addr_o,
output logic [ExtWLEN-1:0] dmem_wdata_o,
output logic [ExtWLEN-1:0] dmem_wmask_o,
output logic [BaseWordsPerWLEN-1:0] dmem_rmask_o,
input logic [ExtWLEN-1:0] dmem_rdata_i,
input logic dmem_rvalid_i,
input logic dmem_rerror_i,
input logic lsu_load_req_i,
input logic lsu_store_req_i,
input insn_subset_e lsu_req_subset_i,
input logic [DmemAddrWidth-1:0] lsu_addr_i,
input logic [BaseIntgWidth-1:0] lsu_base_wdata_i,
input logic [ExtWLEN-1:0] lsu_bignum_wdata_i,
output logic [BaseIntgWidth-1:0] lsu_base_rdata_o,
output logic [ExtWLEN-1:0] lsu_bignum_rdata_o,
output logic lsu_rdata_err_o
);
localparam int BaseWordsPerWLen = WLEN / 32;
localparam int BaseWordAddrW = prim_util_pkg::vbits(WLEN / 8);
// Produce a WLEN bit mask for 32-bit writes given the 32-bit word write address. This doesn't
// propagate X so a separate assertion must be used to check the input isn't X when a valid output
// is desired.
function automatic logic [ExtWLEN-1:0] wmask_from_word_addr(logic [BaseWordAddrW-1:2] addr);
logic [ExtWLEN-1:0] mask;
mask = '0;
// Use of logic == int comparison in this loop works as BaseWordsPerWLen is a constant, so the
// loop can be unrolled. Due to the use of '==' any X or Z in addr will result in an X result
// for the comparison (so mask will remain 0).
for (int i = 0; i < BaseWordsPerWLen; i++) begin
if (addr == i) begin
mask[i*BaseIntgWidth+:BaseIntgWidth] = '1;
end
end
return mask;
endfunction
function automatic logic [BaseWordsPerWLEN-1:0]
rmask_from_word_addr(logic [BaseWordAddrW-1:2] addr);
logic [BaseWordsPerWLEN-1:0] mask;
mask = '0;
for (int i = 0; i < BaseWordsPerWLen; i++) begin
if (addr == i) begin
mask[i] = 1'b1;
end
end
return mask;
endfunction
logic [BaseWordAddrW-1:2] lsu_word_select;
logic lsu_word_select_en;
assign dmem_req_o = lsu_load_req_i | lsu_store_req_i;
assign dmem_write_o = lsu_store_req_i;
assign dmem_addr_o = lsu_addr_i;
// For base 32-bit writes replicate write data across dmem_wdata. dmem_wmask will be set
// appropriately so only the target word is written.
assign dmem_wdata_o = lsu_req_subset_i == InsnSubsetBase ?
{BaseWordsPerWLen{lsu_base_wdata_i}} : lsu_bignum_wdata_i;
assign dmem_wmask_o = lsu_req_subset_i == InsnSubsetBase ?
wmask_from_word_addr(lsu_addr_i[BaseWordAddrW-1:2]) : {ExtWLEN{1'b1}};
assign dmem_rmask_o = lsu_req_subset_i == InsnSubsetBase ?
rmask_from_word_addr(lsu_addr_i[BaseWordAddrW-1:2]) : {BaseWordsPerWLEN{1'b1}};
// Store a portion of the address to select a 32-bit word from the WLEN load data when it returns
// the cycle following the request.
assign lsu_word_select_en = lsu_load_req_i & (lsu_req_subset_i == InsnSubsetBase);
always_ff @(posedge clk_i) begin
if (lsu_word_select_en) begin
lsu_word_select <= lsu_addr_i[BaseWordAddrW-1:2];
end
end
// From the WLEN word read from DMem select out a 32-bit word for base instructions.
for (genvar i_bit = 0; i_bit < BaseIntgWidth; i_bit++) begin : g_base_rdata
logic [BaseWordsPerWLen-1:0] bit_mux;
for (genvar j_word = 0; j_word < BaseWordsPerWLen; j_word++) begin : g_bit_mux
assign bit_mux[j_word] =
(lsu_word_select == j_word) & dmem_rdata_i[i_bit + j_word * BaseIntgWidth];
end
assign lsu_base_rdata_o[i_bit] = |bit_mux;
end
`ASSERT_KNOWN_IF(LsuAddrKnown, lsu_addr_i, lsu_load_req_i | lsu_store_req_i)
// TODO: Produce an error/alert if this doesn't hold?
`ASSERT(DMemRValidAfterReq, dmem_req_o & ~dmem_write_o |=> dmem_rvalid_i)
assign lsu_bignum_rdata_o = dmem_rdata_i;
assign lsu_rdata_err_o = dmem_rvalid_i & dmem_rerror_i;
// clk_i, rst_ni are only used by assertions
logic unused_clk;
logic unused_rst_n;
assign unused_clk = clk_i;
assign unused_rst_n = rst_ni;
endmodule