blob: 0a9e6778e6ffe512c4a3d479a9411e616e68c8f8 [file] [log] [blame]
Philipp Wagner5c2d2942020-06-22 11:34:00 +01001// 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`include "prim_assert.sv"
6
7/**
8 * OpenTitan Big Number Accelerator (OTBN)
9 */
10module otbn
11 import prim_alert_pkg::*;
12 import otbn_pkg::*;
Pirmin Vogel0193aba2020-08-17 15:10:57 +020013 import otbn_reg_pkg::*;
14#(
Pirmin Vogel69b55a82020-10-01 09:54:39 +020015 parameter regfile_e RegFile = RegFileFF,
Pirmin Vogel0193aba2020-08-17 15:10:57 +020016 parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}
17) (
Philipp Wagner5c2d2942020-06-22 11:34:00 +010018 input clk_i,
19 input rst_ni,
20
21 input tlul_pkg::tl_h2d_t tl_i,
22 output tlul_pkg::tl_d2h_t tl_o,
23
24 // Inter-module signals
25 output logic idle_o,
26
27 // Interrupts
28 output logic intr_done_o,
29 output logic intr_err_o,
30
31 // Alerts
Pirmin Vogel0193aba2020-08-17 15:10:57 +020032 input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i,
33 output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o
Philipp Wagner5c2d2942020-06-22 11:34:00 +010034
35 // CSRNG interface
36 // TODO: Needs to be connected to RNG distribution network (#2638)
37);
38
Philipp Wagner5c2d2942020-06-22 11:34:00 +010039 import prim_util_pkg::vbits;
40
41 // The OTBN_*_SIZE parameters are auto-generated by regtool and come from the
42 // bus window sizes; they are given in bytes.
Michael Schaffner82a936e2020-07-13 16:52:38 -070043 localparam int ImemSizeByte = otbn_reg_pkg::OTBN_IMEM_SIZE;
44 localparam int DmemSizeByte = otbn_reg_pkg::OTBN_DMEM_SIZE;
Philipp Wagner5c2d2942020-06-22 11:34:00 +010045
Michael Schaffner82a936e2020-07-13 16:52:38 -070046 localparam int ImemAddrWidth = vbits(ImemSizeByte);
47 localparam int DmemAddrWidth = vbits(DmemSizeByte);
Philipp Wagner5c2d2942020-06-22 11:34:00 +010048
Stefan Wallentowitz7fe6abc2020-06-25 11:05:29 +020049`ifdef OTBN_MODEL
50 localparam int OTBNModel = 1;
51`else
52 localparam int OTBNModel = 0;
53`endif
54
Philipp Wagner5c2d2942020-06-22 11:34:00 +010055 logic start;
56 logic busy_d, busy_q;
57 logic done;
58
59 logic err_valid;
60 logic [31:0] err_code;
61
62 logic [ImemAddrWidth-1:0] start_addr;
63
64 otbn_reg2hw_t reg2hw;
65 otbn_hw2reg_t hw2reg;
66
67 // Bus device windows, as specified in otbn.hjson
68 typedef enum int {
69 TlWinImem = 0,
70 TlWinDmem = 1
71 } tl_win_e;
72
73 tlul_pkg::tl_h2d_t tl_win_h2d [2];
74 tlul_pkg::tl_d2h_t tl_win_d2h [2];
75
76
77 // Inter-module signals ======================================================
78
79 // TODO: Better define what "idle" means -- only the core, or also the
80 // register interface?
81 assign idle_o = ~busy_q | ~start;
82
83
84 // Interrupts ================================================================
85
86 prim_intr_hw #(
87 .Width(1)
88 ) u_intr_hw_done (
Timothy Chen4ab322a2020-09-11 10:50:50 -070089 .clk_i,
90 .rst_ni,
Philipp Wagner5c2d2942020-06-22 11:34:00 +010091 .event_intr_i (done),
92 .reg2hw_intr_enable_q_i (reg2hw.intr_enable.done.q),
93 .reg2hw_intr_test_q_i (reg2hw.intr_test.done.q),
94 .reg2hw_intr_test_qe_i (reg2hw.intr_test.done.qe),
95 .reg2hw_intr_state_q_i (reg2hw.intr_state.done.q),
96 .hw2reg_intr_state_de_o (hw2reg.intr_state.done.de),
97 .hw2reg_intr_state_d_o (hw2reg.intr_state.done.d),
98 .intr_o (intr_done_o)
99 );
100 prim_intr_hw #(
101 .Width(1)
102 ) u_intr_hw_err (
Timothy Chen4ab322a2020-09-11 10:50:50 -0700103 .clk_i,
104 .rst_ni,
Philipp Wagner5c2d2942020-06-22 11:34:00 +0100105 .event_intr_i (err_valid),
106 .reg2hw_intr_enable_q_i (reg2hw.intr_enable.err.q),
107 .reg2hw_intr_test_q_i (reg2hw.intr_test.err.q),
108 .reg2hw_intr_test_qe_i (reg2hw.intr_test.err.qe),
109 .reg2hw_intr_state_q_i (reg2hw.intr_state.err.q),
110 .hw2reg_intr_state_de_o (hw2reg.intr_state.err.de),
111 .hw2reg_intr_state_d_o (hw2reg.intr_state.err.d),
112 .intr_o (intr_err_o)
113 );
114
115
116 // Registers =================================================================
117
118 otbn_reg_top u_reg (
119 .clk_i,
120 .rst_ni,
121 .tl_i,
122 .tl_o,
123 .tl_win_o (tl_win_h2d),
124 .tl_win_i (tl_win_d2h),
125
126 .reg2hw,
127 .hw2reg,
128
Philipp Wagnera59a0762020-07-18 00:03:59 +0100129 .devmode_i (1'b1)
Philipp Wagner5c2d2942020-06-22 11:34:00 +0100130 );
131
132 // CMD register
133 assign start = reg2hw.cmd.start.qe & reg2hw.cmd.start.q;
134
135 // STATUS register
136 assign hw2reg.status.busy.d = busy_q;
137 assign hw2reg.status.dummy.d = 1'b0;
138
139 // ERR_CODE register
140 assign hw2reg.err_code.de = err_valid;
141 assign hw2reg.err_code.d = err_code;
142
143 // START_ADDR register
144 assign start_addr = reg2hw.start_addr.q[ImemAddrWidth-1:0];
145
146 // Errors ====================================================================
147
148 // err_valid goes high if there is a new error this cycle. This causes the
149 // register block to take a new error code (stored as ERR_CODE) and triggers
150 // an interrupt. To ensure software on the host CPU only sees the first event
151 // in a series, err_valid is squashed if there is an existing error. Software
152 // should read the ERR_CODE register before clearing the interrupt to avoid
153 // race conditions.
154 assign err_valid = ~reg2hw.intr_state.err.q &
155 (1'b0); // TODO: OR error signals here.
156
157 always_comb begin
158 err_code = ErrCodeNoError;
159 unique case (1'b1)
160 // TODO: Add more errors here.
161
162 default: begin
163 err_code = ErrCodeNoError;
164 end
165 endcase
166 end
167
168 // Instruction Memory (IMEM) =================================================
169
Michael Schaffner82a936e2020-07-13 16:52:38 -0700170 localparam int ImemSizeWords = ImemSizeByte / 4;
171 localparam int ImemIndexWidth = vbits(ImemSizeWords);
Philipp Wagner5c2d2942020-06-22 11:34:00 +0100172
173 // Access select to IMEM: core (1), or bus (0)
174 logic imem_access_core;
175
176 logic imem_req;
177 logic imem_write;
178 logic [ImemIndexWidth-1:0] imem_index;
179 logic [31:0] imem_wdata;
180 logic [31:0] imem_wmask;
181 logic [31:0] imem_rdata;
182 logic imem_rvalid;
183 logic [1:0] imem_rerror;
184
185 logic imem_req_core;
186 logic imem_write_core;
187 logic [ImemIndexWidth-1:0] imem_index_core;
188 logic [31:0] imem_wdata_core;
Philipp Wagner5c2d2942020-06-22 11:34:00 +0100189 logic [31:0] imem_rdata_core;
190 logic imem_rvalid_core;
191 logic [1:0] imem_rerror_core;
192
193 logic imem_req_bus;
194 logic imem_write_bus;
195 logic [ImemIndexWidth-1:0] imem_index_bus;
196 logic [31:0] imem_wdata_bus;
197 logic [31:0] imem_wmask_bus;
198 logic [31:0] imem_rdata_bus;
199 logic imem_rvalid_bus;
200 logic [1:0] imem_rerror_bus;
201
202 logic [ImemAddrWidth-1:0] imem_addr_core;
203 assign imem_index_core = imem_addr_core[ImemAddrWidth-1:2];
204
205 logic [1:0] unused_imem_addr_core_wordbits;
206 assign unused_imem_addr_core_wordbits = imem_addr_core[1:0];
207
208 prim_ram_1p_adv #(
209 .Width (32),
210 .Depth (ImemSizeWords),
211 .DataBitsPerMask (32), // Write masks are not supported.
212 .CfgW (8)
213 ) u_imem (
214 .clk_i,
215 .rst_ni,
216 .req_i (imem_req),
217 .write_i (imem_write),
218 .addr_i (imem_index),
219 .wdata_i (imem_wdata),
220 .wmask_i (imem_wmask),
221 .rdata_o (imem_rdata),
222 .rvalid_o (imem_rvalid),
223 .rerror_o (imem_rerror),
224 .cfg_i ('0)
225 );
226
227 // IMEM access from main TL-UL bus
228 logic imem_gnt_bus;
229 assign imem_gnt_bus = imem_req_bus & ~imem_access_core;
230
231 tlul_adapter_sram #(
232 .SramAw (ImemIndexWidth),
233 .SramDw (32),
234 .Outstanding (1),
235 .ByteAccess (0),
236 .ErrOnRead (0)
237 ) u_tlul_adapter_sram_imem (
238 .clk_i,
239 .rst_ni,
240 .tl_i (tl_win_h2d[TlWinImem]),
241 .tl_o (tl_win_d2h[TlWinImem]),
242
243 .req_o (imem_req_bus ),
244 .gnt_i (imem_gnt_bus ),
245 .we_o (imem_write_bus ),
246 .addr_o (imem_index_bus ),
247 .wdata_o (imem_wdata_bus ),
248 .wmask_o (imem_wmask_bus ),
249 .rdata_i (imem_rdata_bus ),
250 .rvalid_i (imem_rvalid_bus),
251 .rerror_i (imem_rerror_bus)
252 );
253
254 // Mux core and bus access into IMEM
255 assign imem_access_core = busy_q;
256
257 assign imem_req = imem_access_core ? imem_req_core : imem_req_bus;
258 assign imem_write = imem_access_core ? imem_write_core : imem_write_bus;
259 assign imem_index = imem_access_core ? imem_index_core : imem_index_bus;
260 assign imem_wdata = imem_access_core ? imem_wdata_core : imem_wdata_bus;
261
Rupert Swarbrickd54a4502020-07-10 12:06:53 +0100262 // The instruction memory only supports 32b word writes, so we hardcode its
263 // wmask here.
264 //
265 // Since this could cause confusion if the bus tried to do a partial write
266 // (which wasn't caught in the TLUL adapter for some reason), we assert that
267 // the wmask signal from the bus is indeed '1 when it requests a write. We
268 // don't have the corresponding check for writes from the core because the
269 // core cannot perform writes (and has no imem_wmask_o port).
Philipp Wagner5c2d2942020-06-22 11:34:00 +0100270 assign imem_wmask = 32'hFFFFFFFF;
Philipp Wagner5c2d2942020-06-22 11:34:00 +0100271 `ASSERT(ImemWmaskBusIsFullWord_A,
Rupert Swarbrickd54a4502020-07-10 12:06:53 +0100272 imem_req_bus && imem_write_bus |-> imem_wmask_bus == 32'hFFFFFFFF)
Philipp Wagner5c2d2942020-06-22 11:34:00 +0100273
274 // Explicitly tie off bus interface during core operation to avoid leaking
275 // the currently executed instruction from IMEM through the bus
276 // unintentionally.
277 assign imem_rdata_bus = !imem_access_core ? imem_rdata : 32'b0;
278 assign imem_rdata_core = imem_rdata;
279
280 assign imem_rvalid_bus = !imem_access_core ? imem_rvalid : 1'b0;
281 assign imem_rvalid_core = imem_access_core ? imem_rvalid : 1'b0;
282
283 // Since rerror depends on rvalid we could save this mux, but could
284 // potentially leak rerror to the bus. Err on the side of caution.
285 assign imem_rerror_bus = !imem_access_core ? imem_rerror : 2'b00;
286 assign imem_rerror_core = imem_rerror;
287
288
289 // Data Memory (DMEM) ========================================================
290
Michael Schaffner82a936e2020-07-13 16:52:38 -0700291 localparam int DmemSizeWords = DmemSizeByte / (WLEN / 8);
292 localparam int DmemIndexWidth = vbits(DmemSizeWords);
Philipp Wagner5c2d2942020-06-22 11:34:00 +0100293
294 // Access select to DMEM: core (1), or bus (0)
295 logic dmem_access_core;
296
297 logic dmem_req;
298 logic dmem_write;
299 logic [DmemIndexWidth-1:0] dmem_index;
300 logic [WLEN-1:0] dmem_wdata;
301 logic [WLEN-1:0] dmem_wmask;
302 logic [WLEN-1:0] dmem_rdata;
303 logic dmem_rvalid;
304 logic [1:0] dmem_rerror;
305
306 logic dmem_req_core;
307 logic dmem_write_core;
308 logic [DmemIndexWidth-1:0] dmem_index_core;
309 logic [WLEN-1:0] dmem_wdata_core;
310 logic [WLEN-1:0] dmem_wmask_core;
311 logic [WLEN-1:0] dmem_rdata_core;
312 logic dmem_rvalid_core;
313 logic [1:0] dmem_rerror_core;
314
315 logic dmem_req_bus;
316 logic dmem_write_bus;
317 logic [DmemIndexWidth-1:0] dmem_index_bus;
318 logic [WLEN-1:0] dmem_wdata_bus;
319 logic [WLEN-1:0] dmem_wmask_bus;
320 logic [WLEN-1:0] dmem_rdata_bus;
321 logic dmem_rvalid_bus;
322 logic [1:0] dmem_rerror_bus;
323
324 logic [DmemAddrWidth-1:0] dmem_addr_core;
325 assign dmem_index_core = dmem_addr_core[DmemAddrWidth-1:DmemAddrWidth-DmemIndexWidth];
326
327 prim_ram_1p_adv #(
328 .Width (WLEN),
329 .Depth (DmemSizeWords),
330 .DataBitsPerMask (32), // 32b write masks for 32b word writes from bus
331 .CfgW (8)
332 ) u_dmem (
333 .clk_i,
334 .rst_ni,
335 .req_i (dmem_req),
336 .write_i (dmem_write),
337 .addr_i (dmem_index),
338 .wdata_i (dmem_wdata),
339 .wmask_i (dmem_wmask),
340 .rdata_o (dmem_rdata),
341 .rvalid_o (dmem_rvalid),
342 .rerror_o (dmem_rerror),
343 .cfg_i ('0)
344 );
345
346 // DMEM access from main TL-UL bus
347 logic dmem_gnt_bus;
348 assign dmem_gnt_bus = dmem_req_bus & ~dmem_access_core;
349
350 tlul_adapter_sram #(
351 .SramAw (DmemIndexWidth),
352 .SramDw (WLEN),
353 .Outstanding (1),
354 .ByteAccess (0),
355 .ErrOnRead (0)
356 ) u_tlul_adapter_sram_dmem (
357 .clk_i,
358 .rst_ni,
359
360 .tl_i (tl_win_h2d[TlWinDmem]),
361 .tl_o (tl_win_d2h[TlWinDmem]),
362
363 .req_o (dmem_req_bus ),
364 .gnt_i (dmem_gnt_bus ),
365 .we_o (dmem_write_bus ),
366 .addr_o (dmem_index_bus ),
367 .wdata_o (dmem_wdata_bus ),
368 .wmask_o (dmem_wmask_bus ),
369 .rdata_i (dmem_rdata_bus ),
370 .rvalid_i (dmem_rvalid_bus),
371 .rerror_i (dmem_rerror_bus)
372 );
373
374 // Mux core and bus access into dmem
375 assign dmem_access_core = busy_q;
376
377 assign dmem_req = dmem_access_core ? dmem_req_core : dmem_req_bus;
378 assign dmem_write = dmem_access_core ? dmem_write_core : dmem_write_bus;
379 assign dmem_wmask = dmem_access_core ? dmem_wmask_core : dmem_wmask_bus;
380 assign dmem_index = dmem_access_core ? dmem_index_core : dmem_index_bus;
381 assign dmem_wdata = dmem_access_core ? dmem_wdata_core : dmem_wdata_bus;
382
383 // Explicitly tie off bus interface during core operation to avoid leaking
384 // DMEM data through the bus unintentionally.
385 assign dmem_rdata_bus = !dmem_access_core ? dmem_rdata : '0;
386 assign dmem_rdata_core = dmem_rdata;
387
388 assign dmem_rvalid_bus = !dmem_access_core ? dmem_rvalid : 1'b0;
389 assign dmem_rvalid_core = dmem_access_core ? dmem_rvalid : 1'b0;
390
391 // Since rerror depends on rvalid we could save this mux, but could
392 // potentially leak rerror to the bus. Err on the side of caution.
393 assign dmem_rerror_bus = !dmem_access_core ? dmem_rerror : 2'b00;
394 assign dmem_rerror_core = dmem_rerror;
395
396
397 // Alerts ====================================================================
398
399 logic [NumAlerts-1:0] alerts;
400 assign alerts[AlertImemUncorrectable] = imem_rerror[1];
401 assign alerts[AlertDmemUncorrectable] = dmem_rerror[1];
402 assign alerts[AlertRegUncorrectable] = 1'b0; // TODO: Implement
403 for (genvar i = 0; i < NumAlerts; i++) begin: gen_alert_tx
404 prim_alert_sender #(
405 .AsyncOn(AlertAsyncOn[i])
406 ) i_prim_alert_sender (
407 .clk_i,
408 .rst_ni,
409 .alert_i (alerts[i] ),
410 .alert_rx_i (alert_rx_i[i]),
411 .alert_tx_o (alert_tx_o[i])
412 );
413 end
414
415
416 // OTBN Core =================================================================
417
418 always_ff @(posedge clk_i or negedge rst_ni) begin
419 if (!rst_ni) begin
420 busy_q <= 1'b0;
421 end else begin
422 busy_q <= busy_d;
423 end
424 end
425 assign busy_d = (busy_q | start) & ~done;
426
Stefan Wallentowitz7fe6abc2020-06-25 11:05:29 +0200427 if (OTBNModel) begin : gen_impl_model
Rupert Swarbrick4c7b0232020-09-23 17:19:56 +0100428 localparam ImemScope = "..u_imem.u_mem.gen_generic.u_impl_generic";
429 localparam DmemScope = "..u_dmem.u_mem.gen_generic.u_impl_generic";
Philipp Wagner5c2d2942020-06-22 11:34:00 +0100430
Stefan Wallentowitz7fe6abc2020-06-25 11:05:29 +0200431 otbn_core_model #(
432 .DmemSizeByte(DmemSizeByte),
433 .ImemSizeByte(ImemSizeByte),
434 .DmemScope(DmemScope),
Rupert Swarbrick913a4832020-09-18 15:00:45 +0100435 .ImemScope(ImemScope),
436 .StandaloneModel(1'b1)
Stefan Wallentowitz7fe6abc2020-06-25 11:05:29 +0200437 ) u_otbn_core_model (
438 .clk_i,
439 .rst_ni,
Philipp Wagner5c2d2942020-06-22 11:34:00 +0100440
Stefan Wallentowitz7fe6abc2020-06-25 11:05:29 +0200441 .start_i (start),
442 .done_o (done),
Philipp Wagner5c2d2942020-06-22 11:34:00 +0100443
Rupert Swarbricke2eb4762020-09-08 14:40:18 +0100444 .start_addr_i (start_addr)
Stefan Wallentowitz7fe6abc2020-06-25 11:05:29 +0200445 );
446 end else begin : gen_impl_rtl
447 otbn_core #(
Pirmin Vogel69b55a82020-10-01 09:54:39 +0200448 .RegFile(RegFile),
Stefan Wallentowitz7fe6abc2020-06-25 11:05:29 +0200449 .DmemSizeByte(DmemSizeByte),
450 .ImemSizeByte(ImemSizeByte)
451 ) u_otbn_core (
452 .clk_i,
453 .rst_ni,
454
455 .start_i (start),
456 .done_o (done),
457
458 .start_addr_i (start_addr),
459
460 .imem_req_o (imem_req_core),
461 .imem_addr_o (imem_addr_core),
462 .imem_wdata_o (imem_wdata_core),
463 .imem_rdata_i (imem_rdata_core),
464 .imem_rvalid_i (imem_rvalid_core),
465 .imem_rerror_i (imem_rerror_core),
466
467 .dmem_req_o (dmem_req_core),
468 .dmem_write_o (dmem_write_core),
469 .dmem_addr_o (dmem_addr_core),
470 .dmem_wdata_o (dmem_wdata_core),
471 .dmem_wmask_o (dmem_wmask_core),
472 .dmem_rdata_i (dmem_rdata_core),
473 .dmem_rvalid_i (dmem_rvalid_core),
474 .dmem_rerror_i (dmem_rerror_core)
475 );
476 end
Philipp Wagner5c2d2942020-06-22 11:34:00 +0100477
Rupert Swarbrickd54a4502020-07-10 12:06:53 +0100478 // The core can never signal a write to IMEM
479 assign imem_write_core = 1'b0;
Philipp Wagner5c2d2942020-06-22 11:34:00 +0100480
481 // LFSR ======================================================================
482
483 // TODO: Potentially insert local LFSR, or use output from RNG distribution
484 // network directly, depending on availability. Revisit once CSRNG interface
485 // is known (#2638).
486
487
488 // Asserts ===================================================================
489
490 // All outputs should be known value after reset
491 `ASSERT_KNOWN(TlODValidKnown_A, tl_o.d_valid)
492 `ASSERT_KNOWN(TlOAReadyKnown_A, tl_o.a_ready)
493 `ASSERT_KNOWN(IntrDoneOKnown_A, intr_done_o)
494 `ASSERT_KNOWN(IntrErrOKnown_A, intr_err_o)
495 `ASSERT_KNOWN(AlertTxOKnown_A, alert_tx_o)
Philipp Wagnerb4c1c8b2020-09-17 15:53:40 +0100496 `ASSERT_KNOWN(IdleOKnown_A, idle_o)
Philipp Wagner5c2d2942020-06-22 11:34:00 +0100497
498endmodule