hw/ip/prim/rtl/prim_packer.sv Cov: 97.3%
1: // Copyright lowRISC contributors.
2: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3: // SPDX-License-Identifier: Apache-2.0
4: //
5: // Combine InW data and write to OutW data if packed to full word or stop signal
6:
7: module prim_packer #(
8: parameter int InW = 32,
9: parameter int OutW = 32
10: ) (
11: input clk_i ,
12: input rst_ni,
13:
14: input valid_i,
15: input [InW-1:0] data_i,
16: input [InW-1:0] mask_i,
17: output ready_o,
18:
19: output logic valid_o,
20: output logic [OutW-1:0] data_o,
21: output logic [OutW-1:0] mask_o,
22: input ready_i,
23:
24: input flush_i, // If 1, send out remnant and clear state
25: output logic flush_done_o
26: );
27:
28: localparam int Width = InW + OutW;
29: localparam int PtrW = $clog2(Width+1);
30: localparam int MaxW = (InW > OutW) ? InW : OutW;
31:
32: logic valid_next, ready_next;
33: logic [MaxW-1:0] stored_data, stored_mask;
34: logic [Width-1:0] concat_data, concat_mask;
35: logic [Width-1:0] shiftl_data, shiftl_mask;
36:
37: logic [PtrW-1:0] pos, pos_next; // Current write position
38: logic [$clog2(InW)-1:0] lod_idx; // result of Leading One Detector
39: logic [$clog2(InW+1)-1:0] inmask_ones; // Counting Ones for mask_i
40:
41: logic ack_in, ack_out;
42:
43: logic flush_ready; // flush_i is pulse, so only when the output is ready flush_ready assets
44:
45: // Computing next position
46: always_comb begin
47: // counting mask_i ones
48: inmask_ones = '0;
49: for (int i = 0 ; i < InW ; i++) begin
50: inmask_ones = inmask_ones + mask_i[i];
51: end
52: end
53:
54: assign pos_next = (valid_i) ? pos + PtrW'(inmask_ones) : pos; // pos always stays (% OutW)
55:
56: always_ff @(posedge clk_i or negedge rst_ni) begin
57: if (!rst_ni) begin
58: pos <= '0;
59: end else if (flush_ready) begin
60: pos <= '0;
61: end else if (ack_out) begin
62: `ASSERT_I(pos_next_gte_outw_p, pos_next >= OutW)
63: pos <= pos_next - OutW;
64: end else if (ack_in) begin
65: pos <= pos_next;
66: end
67: end
68:
69: // Leading one detector for mask_i
70: always_comb begin
71: lod_idx = 0;
72: for (int i = InW-1; i >= 0 ; i--) begin
73: if (mask_i[i] == 1'b1) begin
74: lod_idx = i;
75: end
76: end
77: end
78:
79: assign ack_in = valid_i & ready_o;
80: assign ack_out = valid_o & ready_i;
81:
82: // Data process
83: assign shiftl_data = (valid_i) ? Width'(data_i >> lod_idx) << pos : '0;
84: assign shiftl_mask = (valid_i) ? Width'(mask_i >> lod_idx) << pos : '0;
85: assign concat_data = {{(Width-MaxW){1'b0}}, stored_data & stored_mask} |
86: (shiftl_data & shiftl_mask);
87: assign concat_mask = {{(Width-MaxW){1'b0}}, stored_mask} | shiftl_mask;
88:
89: logic [MaxW-1:0] stored_data_next, stored_mask_next;
90:
91: if (InW >= OutW) begin : gen_stored_in
92: assign stored_data_next = concat_data[OutW+:InW];
93: assign stored_mask_next = concat_mask[OutW+:InW];
94: end else begin : gen_stored_out
95: assign stored_data_next = {{(OutW-InW){1'b0}}, concat_data[OutW+:InW]};
96: assign stored_mask_next = {{(OutW-InW){1'b0}}, concat_mask[OutW+:InW]};
97: end
98:
99: // Store the data temporary if it doesn't exceed OutW
100: always_ff @(posedge clk_i or negedge rst_ni) begin
101: if (!rst_ni) begin
102: stored_data <= '0;
103: stored_mask <= '0;
104: end else if (flush_ready) begin
105: stored_data <= '0;
106: stored_mask <= '0;
107: end else if (ack_out) begin
108: stored_data <= stored_data_next;
109: stored_mask <= stored_mask_next;
110: end else if (ack_in) begin
111: // When the requested size is smaller than OutW or output isn't ready
112: // Assume when output isn't ready, the module holds the input request
113: stored_data <= concat_data[MaxW-1:0];
114: stored_mask <= concat_mask[MaxW-1:0];
115: end
116: end
117:
118: // flush_ready handling
119: typedef enum logic {
120: FlushIdle,
121: FlushWait
122: } flush_st_e;
123: flush_st_e flush_st, flush_st_next;
124:
125: always_ff @(posedge clk_i or negedge rst_ni) begin
126: if (!rst_ni) begin
127: flush_st <= FlushIdle;
128: end else begin
129: flush_st <= flush_st_next;
130: end
131: end
132:
133: always_comb begin
134: flush_st_next = FlushIdle;
135:
136: flush_ready = 1'b0;
137:
138: unique case (flush_st)
139: FlushIdle: begin
140: if (flush_i && !ready_i) begin
141: // Wait until hold released
142: flush_st_next = FlushWait;
143:
144: flush_ready = 1'b0;
145: end else if (flush_i && ready_i) begin
146: // Can write right away!
147: flush_st_next = FlushIdle;
148:
149: flush_ready = 1'b1;
150: end else begin
151: flush_st_next = FlushIdle;
152: end
153: end
154:
155: FlushWait: begin
156: // TODO: Add timeout and force flush
157: if (ready_i) begin
158: // Ready to write
159: flush_st_next = FlushIdle;
160:
161: flush_ready = 1'b1;
162: end else begin
163: // Wait ...
164: flush_st_next = FlushWait;
165:
166: flush_ready = 1'b0;
167: end
168: end
169:
170: default: begin
171: flush_st_next = FlushIdle;
172:
173: flush_ready = 1'b0;
174: end
175: endcase
176: end
177:
178: assign flush_done_o = flush_ready;
179:
180: assign valid_next = (pos_next >= OutW) ? 1'b 1 : flush_ready & (pos != '0);
181: assign ready_next = ack_out ? 1'b1 : pos_next <= MaxW; // New `we` needs to be hold.
182:
183: // Output request
184: assign valid_o = valid_next;
185: assign data_o = concat_data[OutW-1:0];
186: assign mask_o = concat_mask[OutW-1:0];
187:
188: // ready_o
189: assign ready_o = ready_next;
190:
191: // TODO: Implement Pipelined logic
192: // Need to change pos logic, mask&data calculation logic too
193:
194: //////////////////////////////////////////////
195: // Assertions, Assumptions, and Coverpoints //
196: //////////////////////////////////////////////
197: // Assumption: mask_i should be contiguous ones
198: // e.g: 0011100 --> OK
199: // 0100011 --> Not OK
200: `ASSUME(ContiguousOnesMask_M,
201: valid_i |-> $countones(mask_i ^ {mask_i[InW-2:0],1'b0}) <= 2,
202: clk_i, !rst_ni)
203:
204: // Assume data pattern to reduce FPV test time
205: //`ASSUME_FPV(FpvDataWithin_M,
206: // data_i inside {'0, '1, 32'hDEAD_BEEF},
207: // clk_i, !rst_ni)
208:
209: // Flush and Write Enable cannot be asserted same time
210: `ASSUME(ExFlushValid_M, flush_i |-> !valid_i, clk_i, !rst_ni)
211:
212: // While in flush state, new request shouldn't come
213: `ASSUME(ValidIDeassertedOnFlush_M,
214: flush_st == FlushWait |-> $stable(valid_i),
215: clk_i, !rst_ni)
216:
217: // If not acked, input port keeps asserting valid and data
218: `ASSUME(DataIStable_M,
219: ##1 valid_i && $past(valid_i) && !$past(ready_o)
220: |-> $stable(data_i) && $stable(mask_i),
221: clk_i, !rst_ni)
222: `ASSUME(ValidIPairedWithReadyO_M,
223: valid_i && !ready_o |=> valid_i,
224: clk_i, !rst_ni)
225:
226: `ASSERT(FlushFollowedByDone_A,
227: ##1 $rose(flush_i) && !flush_done_o |-> !flush_done_o [*0:$] ##1 flush_done_o,
228: clk_i, !rst_ni)
229:
230: // If not acked, valid_o should keep asserting
231: `ASSERT(ValidOPairedWidthReadyI_A,
232: valid_o && !ready_i |=> valid_o,
233: clk_i, !rst_ni)
234:
235: // If input mask + stored data is greater than output width, valid should be asserted
236: `ASSERT(ValidOAssertedForInputGTEOutW_A,
237: valid_i && (($countones(mask_i) + $countones(stored_mask)) >= OutW) |-> valid_o,
238: clk_i, !rst_ni)
239:
240: // If output port doesn't accept the data, the data should be stable
241: `ASSERT(DataOStableWhenPending_A,
242: ##1 valid_o && $past(valid_o)
243: && !$past(ready_i) |-> $stable(data_o),
244: clk_i, !rst_ni)
245:
246: // If input data & stored data are greater than OutW, remained should be stored
247: // TODO: Find out how the FPV time can be reduced.
248: //`ASSERT(ExcessiveDataStored_A,
249: // ack_in && (($countones(mask_i) + $countones(stored_mask)) > OutW) |=>
250: // (($past(data_i) & $past(mask_i)) >>
251: // ($past(lod_idx)+OutW-$countones($past(stored_mask))))
252: // == stored_data,
253: // clk_i, !rst_ni)
254: `ASSERT(ExcessiveMaskStored_A,
255: ack_in && (($countones(mask_i) + $countones(stored_mask)) > OutW) |=>
256: ($past(mask_i) >>
257: ($past(lod_idx)+OutW-$countones($past(stored_mask))))
258: == stored_mask,
259: clk_i, !rst_ni)
260:
261: endmodule
262: