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