| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| // KMAC MSG_FIFO |
| // |
| // This module converts TL-UL interface into MSG_FIFO interface used in KMAC. |
| |
| `include "prim_assert.sv" |
| |
| module kmac_msgfifo |
| import kmac_pkg::*; |
| #( |
| // OutWidth is MsgFIFO data width. prim_packer converts InW to OutW prior to |
| // pushing to MsgFIFO |
| parameter int OutWidth = 64, |
| |
| // Internal MsgFIFO Entry count |
| parameter int MsgDepth = 9, |
| localparam int MsgDepthW = $clog2(MsgDepth+1) // derived parameter |
| ) ( |
| input clk_i, |
| input rst_ni, |
| |
| // from REG or KeyMgr Intf input |
| input fifo_valid_i, |
| input [OutWidth-1:0] fifo_data_i, |
| input [OutWidth-1:0] fifo_mask_i, |
| output fifo_ready_o, |
| |
| // MSG interface |
| output logic msg_valid_o, |
| output logic [OutWidth-1:0] msg_data_o, |
| output logic [OutWidth/8-1:0] msg_strb_o, |
| input msg_ready_i, |
| |
| output logic fifo_empty_o, |
| output logic fifo_full_o, |
| output logic [MsgDepthW-1:0] fifo_depth_o, |
| |
| // Control |
| input clear_i, |
| |
| // process_i --> process_o |
| // process_o asserted after all internal messages are flushed out to MSG interface |
| input process_i, |
| output logic process_o |
| ); |
| |
| ///////////////// |
| // Definitions // |
| ///////////////// |
| typedef struct packed { |
| logic [OutWidth-1:0] data; |
| logic [OutWidth/8-1:0] strb; // one bit per byte |
| } fifo_t; |
| |
| typedef enum logic [1:0] { |
| // In Idle, it checks if process input received or not. |
| // If received, the signal goes to packer and flush internal pending data |
| FlushIdle, |
| |
| // In Packer state, it waits the packer flush operation completes. |
| // The flush_done signal do nothing but after this, it is assumed that |
| // MSG FIFO received the request. |
| FlushPacker, |
| |
| // In Fifo, it waits until MsgFifo is empty. Then asserts process_o |
| FlushFifo, |
| |
| // After flushing, it waits the done (clear) signal. It is assumed that |
| // no incoming messages are transmitted between `process_i` and `clear_i` |
| FlushClear |
| } flush_st_e; |
| |
| ///////////// |
| // Signals // |
| ///////////// |
| |
| // Packer write path |
| logic packer_wvalid; |
| logic [OutWidth-1:0] packer_wdata; |
| logic [OutWidth-1:0] packer_wmask; |
| logic packer_wready; |
| |
| // Message FIFO signals |
| logic fifo_wvalid; |
| fifo_t fifo_wdata; |
| logic fifo_wready; |
| logic fifo_rvalid; |
| fifo_t fifo_rdata; |
| logic fifo_rready; |
| |
| // packer flush to msg_fifo, then msg_fifo empty out the internals |
| // then assert msgfifo_flush_done |
| logic packer_flush_done; |
| logic msgfifo_flush_done; |
| |
| prim_packer #( |
| .InW (OutWidth), |
| .OutW (OutWidth), |
| .HintByteData (1) |
| ) u_packer ( |
| .clk_i, |
| .rst_ni, |
| |
| .valid_i (fifo_valid_i), |
| .data_i (fifo_data_i), |
| .mask_i (fifo_mask_i), |
| .ready_o (fifo_ready_o), |
| |
| .valid_o (packer_wvalid), |
| .data_o (packer_wdata), |
| .mask_o (packer_wmask), |
| .ready_i (packer_wready), |
| |
| .flush_i (process_i), |
| .flush_done_o (packer_flush_done) |
| ); |
| |
| // Assign packer wdata and wmask to FIFO struct |
| // In contrast to HMAC case, KMAC SHA3 operates in little-endian. MSG fifo is |
| // converted into 3-D form so the endianess here is not a problem. |
| assign fifo_wdata.data = packer_wdata; |
| always_comb begin |
| fifo_wdata.strb = '0; |
| for (int i = 0 ; i < OutWidth/8 ; i++) begin |
| fifo_wdata.strb[i] = packer_wmask[8*i]; |
| end |
| end |
| |
| // MsgFIFO |
| prim_fifo_sync #( |
| .Width ($bits(fifo_t)), |
| .Pass (1'b 1), |
| .Depth (MsgDepth) |
| ) u_msgfifo ( |
| .clk_i, |
| .rst_ni, |
| .clr_i (clear_i), |
| |
| .wvalid_i(fifo_wvalid), |
| .wready_o(fifo_wready), |
| .wdata_i (fifo_wdata), |
| |
| .depth_o (fifo_depth_o), |
| .full_o (fifo_full_o), |
| |
| .rvalid_o (fifo_rvalid), |
| .rready_i (fifo_rready), |
| .rdata_o (fifo_rdata), |
| .err_o () |
| |
| ); |
| |
| assign fifo_wvalid = packer_wvalid; |
| assign packer_wready = fifo_wready; |
| |
| assign msg_valid_o = fifo_rvalid; |
| assign fifo_rready = msg_ready_i; |
| assign msg_data_o = fifo_rdata.data; |
| assign msg_strb_o = fifo_rdata.strb; |
| |
| assign fifo_empty_o = !fifo_rvalid; |
| |
| // Flush (process from outside) handling |
| flush_st_e flush_st, flush_st_d; |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| flush_st <= FlushIdle; |
| end else begin |
| flush_st <= flush_st_d; |
| end |
| end |
| |
| always_comb begin |
| flush_st_d = FlushIdle; |
| |
| msgfifo_flush_done = 1'b 0; |
| |
| unique case (flush_st) |
| FlushIdle: begin |
| if (process_i) begin |
| flush_st_d = FlushPacker; |
| end else begin |
| flush_st_d = FlushIdle; |
| end |
| end |
| |
| FlushPacker: begin |
| if (packer_flush_done) begin |
| flush_st_d = FlushFifo; |
| end else begin |
| flush_st_d = FlushPacker; |
| end |
| end |
| |
| FlushFifo: begin |
| if (fifo_empty_o) begin |
| flush_st_d = FlushClear; |
| |
| msgfifo_flush_done = 1'b 1; |
| end else begin |
| flush_st_d = FlushFifo; |
| end |
| end |
| |
| FlushClear: begin |
| if (clear_i) begin |
| flush_st_d = FlushIdle; |
| end else begin |
| flush_st_d = FlushClear; |
| end |
| end |
| |
| default: begin |
| flush_st_d = FlushIdle; |
| end |
| endcase |
| end |
| |
| assign process_o = msgfifo_flush_done; |
| |
| //////////////// |
| // Assertions // |
| //////////////// |
| |
| // Flush state known checker |
| `ASSERT(FlushStInValid_A, flush_st inside {FlushIdle, FlushPacker, FlushFifo, FlushClear}) |
| |
| // Packer done signal is asserted at least one cycle later |
| `ASSERT(PackerDoneDelay_A, $onehot0({process_i, packer_flush_done})) |
| |
| // process_i not asserted during the flush operation |
| `ASSUME(PackerDoneValid_a, process_i |-> flush_st == FlushIdle) |
| |
| // No messages in between `process_i` and `clear_i` |
| `ASSUME(MessageValid_a, fifo_valid_i |-> flush_st == FlushIdle) |
| |
| endmodule |