//======================================================================
//
// Copyright: 2019, The Commons Conservancy Cryptech Project
// SPDX-License-Identifier: BSD-3-Clause
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// - Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// - Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//======================================================================
`timescale 1ns / 1ps
module tb_core_full_512;
//
// Headers
//
`include "../rtl/modexpng_parameters.vh"
//
// Test Vectors
//
localparam TB_MODULUS_LENGTH_N = 512;
localparam TB_MODULUS_LENGTH_PQ = TB_MODULUS_LENGTH_N / 2;
localparam TB_NUM_WORDS_PQ = TB_MODULUS_LENGTH_PQ / BUS_DATA_W;
localparam TB_NUM_WORDS_N = TB_MODULUS_LENGTH_N / BUS_DATA_W;
localparam CORE_NUM_WORDS_PQ = TB_MODULUS_LENGTH_PQ / WORD_W;
localparam CORE_NUM_WORDS_N = TB_MODULUS_LENGTH_N / WORD_W;
reg [31:0] M[0:TB_NUM_WORDS_N-1];
reg [31:0] N[0:TB_NUM_WORDS_N-1];
reg [31:0] N_FACTOR[0:TB_NUM_WORDS_N-1];
reg [31:0] N_COEFF[0:TB_NUM_WORDS_N];
reg [31:0] X[0:TB_NUM_WORDS_N-1];
reg [31:0] Y[0:TB_NUM_WORDS_N-1];
reg [31:0] P[0:TB_NUM_WORDS_PQ-1];
reg [31:0] Q[0:TB_NUM_WORDS_PQ-1];
reg [31:0] P_FACTOR[0:TB_NUM_WORDS_PQ-1];
reg [31:0] Q_FACTOR[0:TB_NUM_WORDS_PQ-1];
reg [31:0] P_COEFF[0:TB_NUM_WORDS_PQ];
reg [31:0] Q_COEFF[0:TB_NUM_WORDS_PQ];
reg [31:0] D[0:TB_NUM_WORDS_N-1];
reg [31:0] DP[0:TB_NUM_WORDS_PQ-1];
reg [31:0] DQ[0:TB_NUM_WORDS_PQ-1];
reg [31:0] QINV[0:TB_NUM_WORDS_PQ-1];
reg [31:0] XM[0:TB_NUM_WORDS_N-1];
reg [31:0] YM[0:TB_NUM_WORDS_N-1];
reg [31:0] S[0:TB_NUM_WORDS_N-1];
reg [31:0] XM_READBACK[0:TB_NUM_WORDS_N-1];
reg [31:0] YM_READBACK[0:TB_NUM_WORDS_N-1];
reg [31:0] S_READBACK[0:TB_NUM_WORDS_N-1];
initial begin
M[ 0] = 32'h8d3b583b; M[ 1] = 32'hc370f07e; M[ 2] = 32'hb9078738; M[ 3] = 32'haf37f86c;
M[ 4] = 32'h02f0e161; M[ 5] = 32'h0506a68a; M[ 6] = 32'h1ae65107; M[ 7] = 32'hcd3a97f1;
M[ 8] = 32'hb27244b8; M[ 9] = 32'h9bc3c400; M[ 10] = 32'he4d5636e; M[ 11] = 32'h35187c07;
M[ 12] = 32'h78a661c9; M[ 13] = 32'h1e7ec273; M[ 14] = 32'hcdc31041; M[ 15] = 32'h002291d8;
N[ 0] = 32'hcb703101; N[ 1] = 32'h82bc8290; N[ 2] = 32'hdb2372c2; N[ 3] = 32'hdeeb692e;
N[ 4] = 32'ha3ee352a; N[ 5] = 32'h81a711ba; N[ 6] = 32'h14ee23bd; N[ 7] = 32'h8ad351c0;
N[ 8] = 32'h75ecd3d5; N[ 9] = 32'h51c9b22f; N[ 10] = 32'hc1d3496e; N[ 11] = 32'h48176f3e;
N[ 12] = 32'hd2aca749; N[ 13] = 32'hf236cea9; N[ 14] = 32'h7f4525ed; N[ 15] = 32'hb4fc5067;
N_FACTOR[ 0] = 32'he253bfbf; N_FACTOR[ 1] = 32'h8e0b26aa; N_FACTOR[ 2] = 32'h0480b661; N_FACTOR[ 3] = 32'h9a13f7a1;
N_FACTOR[ 4] = 32'h464b7342; N_FACTOR[ 5] = 32'hfb6f8e41; N_FACTOR[ 6] = 32'h081208e4; N_FACTOR[ 7] = 32'h63d8328a;
N_FACTOR[ 8] = 32'h604d2b71; N_FACTOR[ 9] = 32'hc987dabe; N_FACTOR[ 10] = 32'h8a474e35; N_FACTOR[ 11] = 32'hc053ba1c;
N_FACTOR[ 12] = 32'h15b82dd9; N_FACTOR[ 13] = 32'h42c2bbfa; N_FACTOR[ 14] = 32'h1681e95d; N_FACTOR[ 15] = 32'h07dee5fa;
N_COEFF[ 0] = 32'h730f30ff; N_COEFF[ 1] = 32'h50ed900a; N_COEFF[ 2] = 32'h0b9038c5; N_COEFF[ 3] = 32'h974ddd03;
N_COEFF[ 4] = 32'he2c118c8; N_COEFF[ 5] = 32'hbe1bc7e1; N_COEFF[ 6] = 32'h224d548c; N_COEFF[ 7] = 32'h48ea2ee4;
N_COEFF[ 8] = 32'heb379247; N_COEFF[ 9] = 32'had97b934; N_COEFF[ 10] = 32'hfc6dfd93; N_COEFF[ 11] = 32'h3a0246ef;
N_COEFF[ 12] = 32'h1baa167c; N_COEFF[ 13] = 32'h7d7ee254; N_COEFF[ 14] = 32'h657f0a53; N_COEFF[ 15] = 32'hea9e7245;
N_COEFF[ 16] = 32'h0000f88c;
X[ 0] = 32'h2d532c22; X[ 1] = 32'h2d3c3b06; X[ 2] = 32'he2862a8f; X[ 3] = 32'he8616ce4;
X[ 4] = 32'h5d77ee51; X[ 5] = 32'he609de07; X[ 6] = 32'hef718044; X[ 7] = 32'h82f35f8b;
X[ 8] = 32'hcdb9dcfe; X[ 9] = 32'hff6ea364; X[ 10] = 32'h0994ae28; X[ 11] = 32'h409b369b;
X[ 12] = 32'hcfabda4e; X[ 13] = 32'h5cd52bbc; X[ 14] = 32'hd90e1715; X[ 15] = 32'h00f4dcf2;
Y[ 0] = 32'h34fff653; Y[ 1] = 32'h50f52544; Y[ 2] = 32'h0ebf96a7; Y[ 3] = 32'h98352265;
Y[ 4] = 32'hbe372927; Y[ 5] = 32'h5b2f6394; Y[ 6] = 32'h9acfccb3; Y[ 7] = 32'h7b5bd4b2;
Y[ 8] = 32'h79b09448; Y[ 9] = 32'h08f11fa6; Y[ 10] = 32'h8411d066; Y[ 11] = 32'h58ba5021;
Y[ 12] = 32'h03c1cb72; Y[ 13] = 32'hacf0689d; Y[ 14] = 32'h983c65bd; Y[ 15] = 32'h29a39dcc;
P[ 0] = 32'hebfc2433; P[ 1] = 32'ha2cfbc81; P[ 2] = 32'hea08812b; P[ 3] = 32'h0adf004f;
P[ 4] = 32'hb987a8c6; P[ 5] = 32'h2860f873; P[ 6] = 32'haf2cfe12; P[ 7] = 32'hddd53c3a;
Q[ 0] = 32'hdc1981fb; Q[ 1] = 32'h01184053; Q[ 2] = 32'h7ab8d640; Q[ 3] = 32'h62ba8a22;
Q[ 4] = 32'h6cb226a1; Q[ 5] = 32'he1f08e16; Q[ 6] = 32'h13e990b5; Q[ 7] = 32'hd0dc7ce3;
P_FACTOR[ 0] = 32'h043fb284; P_FACTOR[ 1] = 32'hcaab7ce3; P_FACTOR[ 2] = 32'h543c62ef; P_FACTOR[ 3] = 32'h8aa74942;
P_FACTOR[ 4] = 32'hefa2ea7b; P_FACTOR[ 5] = 32'hdb8513b5; P_FACTOR[ 6] = 32'h0ea607a4; P_FACTOR[ 7] = 32'h6a59e5a7;
Q_FACTOR[ 0] = 32'h089dcd43; Q_FACTOR[ 1] = 32'h5b23611b; Q_FACTOR[ 2] = 32'h02f0f47c; Q_FACTOR[ 3] = 32'h952ababd;
Q_FACTOR[ 4] = 32'hc4ee13fe; Q_FACTOR[ 5] = 32'h3feb46fa; Q_FACTOR[ 6] = 32'h96b679df; Q_FACTOR[ 7] = 32'h831126dd;
P_COEFF[ 0] = 32'h647c8905; P_COEFF[ 1] = 32'hcb7c6b7d; P_COEFF[ 2] = 32'h8053b8be; P_COEFF[ 3] = 32'hb28f33a7;
P_COEFF[ 4] = 32'hb3207e05; P_COEFF[ 5] = 32'h4e3d416e; P_COEFF[ 6] = 32'h1911d8d9; P_COEFF[ 7] = 32'hd569156e;
P_COEFF[ 8] = 32'h00003dd7;
Q_COEFF[ 0] = 32'h5eee9ecd; Q_COEFF[ 1] = 32'h085153b0; Q_COEFF[ 2] = 32'h85326da6; Q_COEFF[ 3] = 32'h7521931a;
Q_COEFF[ 4] = 32'h99e0eef1; Q_COEFF[ 5] = 32'ha219917b; Q_COEFF[ 6] = 32'he8e9087a; Q_COEFF[ 7] = 32'h5239d12b;
Q_COEFF[ 8] = 32'h0000ed92;
D[ 0] = 32'hf127ca41; D[ 1] = 32'hc4975ff0; D[ 2] = 32'h69ebbe13; D[ 3] = 32'h66fe0018;
D[ 4] = 32'hf2089237; D[ 5] = 32'hfa3f05ab; D[ 6] = 32'h2ab183c4; D[ 7] = 32'h1e4b3c04;
D[ 8] = 32'ha67974e8; D[ 9] = 32'ha6714d63; D[ 10] = 32'hfe5cd801; D[ 11] = 32'h13f2071a;
D[ 12] = 32'h0b978309; D[ 13] = 32'hb0ddb4a0; D[ 14] = 32'ha437a2cc; D[ 15] = 32'h2391b2fb;
DP[ 0] = 32'h3891ed91; DP[ 1] = 32'h775046c2; DP[ 2] = 32'h60180c26; DP[ 3] = 32'h5130700a;
DP[ 4] = 32'hb13c8216; DP[ 5] = 32'h833fcf78; DP[ 6] = 32'h7ab89b12; DP[ 7] = 32'hb976758c;
DQ[ 0] = 32'h28cc59ad; DQ[ 1] = 32'h3ce6ed45; DQ[ 2] = 32'ha1f53aeb; DQ[ 3] = 32'h06ca05e1;
DQ[ 4] = 32'hc5195df6; DQ[ 5] = 32'h42cf91f8; DQ[ 6] = 32'h93d6f054; DQ[ 7] = 32'h3d3bc769;
QINV[ 0] = 32'h50201af6; QINV[ 1] = 32'h85d97b7f; QINV[ 2] = 32'h4247e697; QINV[ 3] = 32'h9fd231fe;
QINV[ 4] = 32'h21e98610; QINV[ 5] = 32'ha0bc58dc; QINV[ 6] = 32'ha86f266c; QINV[ 7] = 32'h838688c8;
XM[ 0] = 32'hf9980f33; XM[ 1] = 32'hb444f483; XM[ 2] = 32'h0a6f8294; XM[ 3] = 32'h1c74da49;
XM[ 4] = 32'h0aa4151f; XM[ 5] = 32'ha1dfb66f; XM[ 6] = 32'h1415da79; XM[ 7] = 32'had7d3272;
XM[ 8] = 32'h43d7b612; XM[ 9] = 32'h56626cce; XM[ 10] = 32'ha65edef6; XM[ 11] = 32'h28c49eb8;
XM[ 12] = 32'h5364b7f8; XM[ 13] = 32'hd170915e; XM[ 14] = 32'h5a4c960d; XM[ 15] = 32'h27cb1911;
YM[ 0] = 32'h7640c2ca; YM[ 1] = 32'hf49d583a; YM[ 2] = 32'he4ae0f22; YM[ 3] = 32'ha3dad5ed;
YM[ 4] = 32'hbe88ab4d; YM[ 5] = 32'h9fb50b38; YM[ 6] = 32'h223feceb; YM[ 7] = 32'hfc4893ff;
YM[ 8] = 32'hb40556c2; YM[ 9] = 32'hb25b27fa; YM[ 10] = 32'h7e277535; YM[ 11] = 32'h42e1e9ab;
YM[ 12] = 32'hebd55ef2; YM[ 13] = 32'h8b6d8c0b; YM[ 14] = 32'h4d91ad9a; YM[ 15] = 32'h0e8bf565;
S[ 0] = 32'h2f89f059; S[ 1] = 32'hdbc41170; S[ 2] = 32'h1d7ea6c0; S[ 3] = 32'h1df9add6;
S[ 4] = 32'ha619e2e1; S[ 5] = 32'h253fcd88; S[ 6] = 32'h6c03a351; S[ 7] = 32'h795b1df0;
S[ 8] = 32'h2854a51a; S[ 9] = 32'h0245619b; S[ 10] = 32'hfb67ef8f; S[ 11] = 32'hcc5bdd4f;
S[ 12] = 32'ha70f58bd; S[ 13] = 32'h31f15702; S[ 14] = 32'hd6f36259; S[ 15] = 32'h280e67e0;
end
//
// Clocks
//
`define CLK_FREQUENCY_MHZ (100.0)
`define CLK_PERIOD_NS (1000.0 / `CLK_FREQUENCY_MHZ)
`define CLK_PERIOD_HALF_NS (0.5 * `CLK_PERIOD_NS)
`define CLK_PERIOD_QUARTER_NS (0.5 * `CLK_PERIOD_HALF_NS)
`define CLK_BUS_FREQUENCY_MHZ (25.0)
`define CLK_BUS_PERIOD_NS (1000.0 / `CLK_BUS_FREQUENCY_MHZ)
`define CLK_BUS_PERIOD_HALF_NS (0.5 * `CLK_BUS_PERIOD_NS)
reg clk = 1'b1;
reg clk_dly = 1'b0;
wire clk_idle = clk & clk_dly;
reg clk_bus = 1'b1;
reg clk_bus_dly = 1'b0;
wire clk_bus_idle = clk_bus & clk_bus_dly;
always #`CLK_PERIOD_HALF_NS clk <= ~clk;
always #`CLK_BUS_PERIOD_HALF_NS clk_bus <= ~clk_bus;
always @(clk ) clk_dly <= #(`CLK_PERIOD_HALF_NS - `CLK_PERIOD_QUARTER_NS) clk;
always @(clk_bus) clk_bus_dly <= #(`CLK_BUS_PERIOD_HALF_NS - `CLK_PERIOD_QUARTER_NS) clk_bus;
//
// Clock Sync
//
task sync_clk;
while (clk_idle !== 1) _wait_quarter_clk_tick;
endtask
task sync_clk_bus;
while (clk_bus_idle !== 1) _wait_quarter_clk_tick;
endtask
//
// Reset
//
reg rst = 1'b1;
wire rst_n = ~rst;
//
// Control / Status
//
reg [ 7:0] word_index_last_n;
reg [ 7:0] word_index_last_pq;
reg [11:0] bit_index_last_n;
reg [11:0] bit_index_last_pq;
reg core_next = 1'b0;
wire core_valid;
reg core_crt_mode;
//
// System Bus
//
reg bus_cs = 1'b0;
reg bus_we = 1'b0;
reg [11:0] bus_addr;
reg [31:0] bus_data_wr;
wire [31:0] bus_data_rd;
wire [ 1:0] bus_addr_sel = bus_addr[11:10];
wire [ 2:0] bus_addr_bank = bus_addr[9:7];
wire [ 6:0] bus_addr_data = bus_addr[6:0];
//
// UUT
//
modexpng_core_top uut
(
.clk (clk),
.clk_bus (clk_bus),
.rst_n (rst_n),
.next (core_next),
.valid (core_valid),
.crt_mode (core_crt_mode),
.word_index_last_n (word_index_last_n),
.word_index_last_pq (word_index_last_pq),
.bit_index_last_n (bit_index_last_n),
.bit_index_last_pq (bit_index_last_pq),
.bus_cs (bus_cs),
.bus_we (bus_we),
.bus_addr (bus_addr),
.bus_data_wr (bus_data_wr),
.bus_data_rd (bus_data_rd)
);
//
// Bus Init Routine
//
task core_set_input;
begin
core_set_input_1;
core_set_input_2;
wait_clk_bus_ticks(10);
$display("Core input banks written.");
end
endtask
//
// Script
//
initial main;
//
// Main Routine (Control/Status, Bus)
//
integer i, j, k;
task main;
begin
sync_clk; // switch to fast core clock
core_reset; // reset core
core_set_params; // set parameters (modulus width, exponent length)
sync_clk_bus; // switch to slow bus clock
core_set_input; // write to core input banks
/**//**/
sync_clk; // switch to fast core clock
core_set_crt_mode(1); // enable CRT signing
core_pulse_next; // assert 'next' bit for one cycle
core_wait_valid; // wait till 'valid' bit gets asserted
sync_clk_bus; // switch to slow bus clock
core_get_output; // read from core output banks
core_verify_output; // check, whether core output matches precomputed known good refrence values
core_print_load; //
/**//**/
sync_clk; // switch to fast core clock
core_set_crt_mode(0); // disable CRT signing
core_pulse_next; // assert 'next' bit for one cycle
core_wait_valid; // wait till 'valid' bit gets asserted
sync_clk_bus; // switch to slow bus clock
core_get_output; // read from core output banks
core_verify_output; // check, whether core output matches precomputed known good refrence values
core_print_load; //
/**//**/
end
endtask
task core_reset;
begin
wait_clk_ticks(100);
rst = 1'b0;
wait_clk_ticks(10);
$display("Core reset finished.");
end
endtask
task core_set_params;
begin
word_index_last_n = CORE_NUM_WORDS_N - 1;
word_index_last_pq = CORE_NUM_WORDS_PQ - 1;
bit_index_last_n = TB_MODULUS_LENGTH_N - 1;
bit_index_last_pq = TB_MODULUS_LENGTH_N / 2 - 1;
$display("Core parameters set.");
end
endtask
task core_set_crt_mode;
input _crt;
begin
core_crt_mode = _crt;
if (_crt) $display("Enabled CRT mode.");
else $display("Disabled CRT mode.");
end
endtask
task core_pulse_next;
begin
core_next = 1'b1;
wait_clk_tick;
core_next = 1'b0;
$display("Pulsed core 'next' control signal.");
end
endtask
task core_wait_valid;
begin
while (!core_valid) wait_clk_tick;
wait_clk_ticks(10);
$display("Detected high core 'valid' status signal.");
end
endtask
//
// Variables
//
integer _w, _n;
//
// core_set_input_1
//
task core_set_input_1;
reg [9:0] _tn;
begin
_tn = BANK_IN_1_N_COEFF * 2 ** BUS_OP_ADDR_W + TB_NUM_WORDS_N; // trick to write extra trailer word
for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_write(2'd1, BANK_IN_1_M, _w[6:0], M[_w]);
for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_write(2'd1, BANK_IN_1_N, _w[6:0], N[_w]);
for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_write(2'd1, BANK_IN_1_N_FACTOR, _w[6:0], N_FACTOR[_w]);
for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_write(2'd1, BANK_IN_1_N_COEFF, _w[6:0], N_COEFF[_w]);
bus_write(2'd1, _tn[9:7], _tn[6:0], N_COEFF[TB_NUM_WORDS_N]);
for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_write(2'd1, BANK_IN_1_X, _w[6:0], X[_w]);
for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_write(2'd1, BANK_IN_1_Y, _w[6:0], Y[_w]);
end
endtask
//
// core_set_input_2
//
task core_set_input_2;
begin
for (_w=0; _w< TB_NUM_WORDS_N; _w=_w+1) bus_write(2'd2, BANK_IN_2_D, { _w[6:0]}, D [_w]);
for (_w=0; _w< TB_NUM_WORDS_PQ; _w=_w+1) bus_write(2'd2, BANK_IN_2_P, {1'b0, _w[5:0]}, P [_w]);
for (_w=0; _w< TB_NUM_WORDS_PQ; _w=_w+1) bus_write(2'd2, BANK_IN_2_P, {1'b1, _w[5:0]}, DP [_w]);
for (_w=0; _w< TB_NUM_WORDS_PQ; _w=_w+1) bus_write(2'd2, BANK_IN_2_P_FACTOR, { _w[6:0]}, P_FACTOR[_w]);
for (_w=0; _w<=TB_NUM_WORDS_PQ; _w=_w+1) bus_write(2'd2, BANK_IN_2_P_COEFF, { _w[6:0]}, P_COEFF [_w]);
for (_w=0; _w< TB_NUM_WORDS_PQ; _w=_w+1) bus_write(2'd2, BANK_IN_2_Q, {1'b0, _w[5:0]}, Q [_w]);
for (_w=0; _w< TB_NUM_WORDS_PQ; _w=_w+1) bus_write(2'd2, BANK_IN_2_Q, {1'b1, _w[5:0]}, DQ [_w]);
for (_w=0; _w< TB_NUM_WORDS_PQ; _w=_w+1) bus_write(2'd2, BANK_IN_2_Q_FACTOR, { _w[6:0]}, Q_FACTOR[_w]);
for (_w=0; _w<=TB_NUM_WORDS_PQ; _w=_w+1) bus_write(2'd2, BANK_IN_2_Q_COEFF, { _w[6:0]}, Q_COEFF [_w]);
for (_w=0; _w< TB_NUM_WORDS_PQ; _w=_w+1) bus_write(2'd2, BANK_IN_2_QINV, { _w[6:0]}, QINV [_w]);
end
endtask
//
// core_get_output
//
task core_get_output;
begin
for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_read(2'd3, BANK_OUT_XM, _w[6:0], XM_READBACK[_w]);
for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_read(2'd3, BANK_OUT_YM, _w[6:0], YM_READBACK[_w]);
for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_read(2'd3, BANK_OUT_S, _w[6:0], S_READBACK[_w]);
wait_clk_bus_ticks(10);
$display("Core output banks read.");
end
endtask
//
// core_verify_output
//
task core_verify_output;
//
reg xm_ok;
reg ym_ok;
reg s_ok;
//
begin
//
xm_ok = 1;
ym_ok = 1;
s_ok = 1;
//
for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) begin
if (XM_READBACK[_w] !== XM[_w]) xm_ok = 0;
if (YM_READBACK[_w] !== YM[_w]) ym_ok = 0;
if (S_READBACK[_w] !== S[_w]) s_ok = 0;
end
//
if (!xm_ok)
//
for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) begin
$write("XM / XM_READBACK [%3d] = 0x%08x / 0x%08x", _w, XM[_w], XM_READBACK[_w]);
if (XM[_w] !== XM_READBACK[_w]) $write(" <???: 0x%08x> ", XM[_w] ^ XM_READBACK[_w]);
$write("\n");
end
//
if (!ym_ok)
//
for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) begin
$write("YM / YM_READBACK [%3d] = 0x%08x / 0x%08x", _w, YM[_w], YM_READBACK[_w]);
if (YM[_w] !== YM_READBACK[_w]) $write(" <???: 0x%08x> ", YM[_w] ^ YM_READBACK[_w]);
$write("\n");
end
//
if (!s_ok)
//
for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) begin
$write("S / S_READBACK [%3d] = 0x%08x / 0x%08x", _w, S[_w], S_READBACK[_w]);
if (S[_w] !== S_READBACK[_w]) $write(" <???: 0x%08x> ", S[_w] ^ S_READBACK[_w]);
$write("\n");
end
//
$write("XM is ");
if (xm_ok) $write("OK.\n");
else $write("WRONG!\n");
//
$write("YM is ");
if (ym_ok) $write("OK.\n");
else $write("WRONG!\n");
//
$write("S is ");
if (s_ok) $write("OK.\n");
else $write("WRONG!\n");
//
end
//
endtask
//
// _bus_drive()
//
task _bus_drive;
input cs;
input we;
input [11:0] addr;
input [31:0] data;
{bus_cs, bus_we, bus_addr, bus_data_wr} <= {cs, we, addr, data};
endtask
//
// bus_write()
//
task bus_write;
input [ 1:0] sel;
input [ 2:0] bank;
input [ 6:0] addr;
input [31:0] data;
begin
_bus_drive(1'b1, 1'b1, {sel, bank, addr}, data);
wait_clk_bus_tick;
_bus_drive(1'b0, 1'b0, 12'hXXX, 32'hXXXXXXXX);
end
endtask
//
// bus_read()
//
task bus_read;
input [ 1:0] sel;
input [ 2:0] bank;
input [ 6:0] addr;
output [31:0] data;
begin
_bus_drive(1'b1, 1'b0, {sel, bank, addr}, 32'hXXXXXXXX);
wait_clk_bus_tick;
data = bus_data_rd;
_bus_drive(1'b0, 1'b0, 12'hXXX, 32'hXXXXXXXX);
end
endtask
//
// _wait_quarter_clk_tick()
//
task _wait_quarter_clk_tick;
#`CLK_PERIOD_QUARTER_NS;
endtask
//
// _wait_half_clk_tick()
//
task _wait_half_clk_tick;
begin
_wait_quarter_clk_tick;
_wait_quarter_clk_tick;
end
endtask
//
// wait_clk_tick()
//
task wait_clk_tick;
begin
_wait_half_clk_tick;
_wait_half_clk_tick;
end
endtask
//
// wait_clk_bus_tick()
//
task wait_clk_bus_tick;
#`CLK_BUS_PERIOD_NS;
endtask
//
// wait_clk_ticks()
//
task wait_clk_ticks;
input integer num_ticks;
for (_n=0; _n<num_ticks; _n=_n+1)
wait_clk_tick;
endtask
//
// wait_clk_bus_ticks()
//
task wait_clk_bus_ticks;
input integer num_ticks;
for (_n=0; _n<num_ticks; _n=_n+1)
wait_clk_bus_tick;
endtask
//
// Multiplier Load Calculator
//
real load_cyc_total_prev = 0.0;
real load_cyc_mult_prev = 0.0;
real load_cyc_total = 0.0;
always @(posedge clk)
//
if (!core_valid)
load_cyc_total <= load_cyc_total + 1.0;
task core_print_load;
real delta_cyc_total, delta_cyc_mult, load_pct;
begin
`ifndef MODEXPNG_ENABLE_DEBUG
$display("core_print_load: Multiplier load was not calculated, since MODEXPNG_ENABLE_DEBUG was no defined.");
`else
delta_cyc_total = load_cyc_total - load_cyc_total_prev;
delta_cyc_mult = uut.mmm_x.load_cyc_mult - load_cyc_mult_prev;
load_pct = 100.0 * delta_cyc_mult / delta_cyc_total;
$display("Multiplier load: %.1f%%", load_pct);
load_cyc_total_prev = load_cyc_total;
load_cyc_mult_prev = uut.mmm_x.load_cyc_mult;
`endif
end
endtask
endmodule