aboutsummaryrefslogblamecommitdiff
path: root/bench/tb_core_full.v
blob: e592ac5ca2be5f0d19203052a44f48abaf939177 (plain) (tree)



















































































































































































































































































                                                                                                                                                                                                                      
                                                            









































































































































































































































                                                                                                                                    
`timescale 1ns / 1ps

module tb_core_full;


    //
    // Headers
    //
    `include "../rtl/modexpng_parameters.vh"


    //
    // Test Vectors
    //
    localparam TB_MODULUS_LENGTH_N  = 1024;
    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] 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'he1b3c6ac; M[  1] = 32'haa2c5d8c; M[  2] = 32'hbecc676a; M[  3] = 32'hda087a3e;
        M[  4] = 32'hf0816496; M[  5] = 32'hf9e17fd8; M[  6] = 32'h304d4896; M[  7] = 32'h81d4e9ab;
        M[  8] = 32'h80eff76c; M[  9] = 32'he5b8f9b6; M[ 10] = 32'h4b1ebe55; M[ 11] = 32'ha1feb9dc;
        M[ 12] = 32'heca4192f; M[ 13] = 32'h6ad6ea8e; M[ 14] = 32'hf34aed05; M[ 15] = 32'had38c275;
        M[ 16] = 32'h8d3b583b; M[ 17] = 32'hc370f07e; M[ 18] = 32'hb9078738; M[ 19] = 32'haf37f86c;
        M[ 20] = 32'h02f0e161; M[ 21] = 32'h0506a68a; M[ 22] = 32'h1ae65107; M[ 23] = 32'hcd3a97f1;
        M[ 24] = 32'hb27244b8; M[ 25] = 32'h9bc3c400; M[ 26] = 32'he4d5636e; M[ 27] = 32'h35187c07;
        M[ 28] = 32'h78a661c9; M[ 29] = 32'h1e7ec273; M[ 30] = 32'hcdc31041; M[ 31] = 32'h002291d8;
        N[  0] = 32'h6719997f; N[  1] = 32'hef2df706; N[  2] = 32'h9ba95792; N[  3] = 32'h747e0580;
        N[  4] = 32'h7507684c; N[  5] = 32'h7a10d0d1; N[  6] = 32'h83a33941; N[  7] = 32'haef9fda5;
        N[  8] = 32'h17972933; N[  9] = 32'h0a98251a; N[ 10] = 32'h7dce3d13; N[ 11] = 32'hdad49a60;
        N[ 12] = 32'h9f98006b; N[ 13] = 32'h46fd4a05; N[ 14] = 32'h51966e1d; N[ 15] = 32'hb1c59fab;
        N[ 16] = 32'h8ab3096e; N[ 17] = 32'hef1f0436; N[ 18] = 32'heeed776f; N[ 19] = 32'h106d9d82;
        N[ 20] = 32'hdd2a44af; N[ 21] = 32'h17c32585; N[ 22] = 32'hc854e454; N[ 23] = 32'h600fb6df;
        N[ 24] = 32'h25c2d4bb; N[ 25] = 32'h5f09d790; N[ 26] = 32'he5a2bb93; N[ 27] = 32'h5bc6b044;
        N[ 28] = 32'h2ecbb15f; N[ 29] = 32'h464817f5; N[ 30] = 32'h05cae32b; N[ 31] = 32'hde97bb85;
        N_FACTOR[  0] = 32'ha06a1113; N_FACTOR[  1] = 32'hc9974806; N_FACTOR[  2] = 32'h572d7a20; N_FACTOR[  3] = 32'h04000838;
        N_FACTOR[  4] = 32'hb275c37a; N_FACTOR[  5] = 32'hea78a046; N_FACTOR[  6] = 32'h029e13b8; N_FACTOR[  7] = 32'hae540753;
        N_FACTOR[  8] = 32'h1e98bc21; N_FACTOR[  9] = 32'h34ede47a; N_FACTOR[ 10] = 32'h0c565ecd; N_FACTOR[ 11] = 32'h027ff3bf;
        N_FACTOR[ 12] = 32'h08290d30; N_FACTOR[ 13] = 32'hb92857df; N_FACTOR[ 14] = 32'he6c59eb3; N_FACTOR[ 15] = 32'h09e53d6a;
        N_FACTOR[ 16] = 32'h980d127e; N_FACTOR[ 17] = 32'h4dd6ced0; N_FACTOR[ 18] = 32'h3b9400d0; N_FACTOR[ 19] = 32'h276c6711;
        N_FACTOR[ 20] = 32'h72eaf2e6; N_FACTOR[ 21] = 32'h749f81eb; N_FACTOR[ 22] = 32'h17b7d05f; N_FACTOR[ 23] = 32'h41a3a2cd;
        N_FACTOR[ 24] = 32'h1ba098f3; N_FACTOR[ 25] = 32'h9b884af9; N_FACTOR[ 26] = 32'hdafd920c; N_FACTOR[ 27] = 32'h7b1f5cc6;
        N_FACTOR[ 28] = 32'hb0a0d098; N_FACTOR[ 29] = 32'h4ee55bcf; N_FACTOR[ 30] = 32'haed9b905; N_FACTOR[ 31] = 32'h42d541fb;
        N_COEFF[  0] = 32'hb383d981; N_COEFF[  1] = 32'h9bf1c20c; N_COEFF[  2] = 32'h268999ff; N_COEFF[  3] = 32'h11a3c01a;
        N_COEFF[  4] = 32'h12665495; N_COEFF[  5] = 32'h515b0d96; N_COEFF[  6] = 32'hb704fb07; N_COEFF[  7] = 32'h8e1bd1d6;
        N_COEFF[  8] = 32'h62c5f506; N_COEFF[  9] = 32'hfdcd0163; N_COEFF[ 10] = 32'h8dd55dee; N_COEFF[ 11] = 32'h6d79c8b1;
        N_COEFF[ 12] = 32'hca16d0b9; N_COEFF[ 13] = 32'h88bead48; N_COEFF[ 14] = 32'hbcdb1e94; N_COEFF[ 15] = 32'h950c171d;
        N_COEFF[ 16] = 32'h4fa810af; N_COEFF[ 17] = 32'h9b63e6d2; N_COEFF[ 18] = 32'ha2d0c26b; N_COEFF[ 19] = 32'hafa1ef25;
        N_COEFF[ 20] = 32'h111bd21e; N_COEFF[ 21] = 32'hc2d896f0; N_COEFF[ 22] = 32'h189dc2cf; N_COEFF[ 23] = 32'h6144156a;
        N_COEFF[ 24] = 32'hd1c67123; N_COEFF[ 25] = 32'ha127e4f3; N_COEFF[ 26] = 32'h40d342ef; N_COEFF[ 27] = 32'hee476d42;
        N_COEFF[ 28] = 32'hee05f26a; N_COEFF[ 29] = 32'h4fc717bd; N_COEFF[ 30] = 32'h6baa4d60; N_COEFF[ 31] = 32'h1d6b10db;
        N_COEFF[ 32] = 32'h00006545;
        X[  0] = 32'ha838f053; X[  1] = 32'h8eb9747c; X[  2] = 32'h5991b9eb; X[  3] = 32'h74e6e776;
        X[  4] = 32'hcb5aa9e2; X[  5] = 32'h7f8083d4; X[  6] = 32'h3f7d47ec; X[  7] = 32'hbd76a787;
        X[  8] = 32'hf4c166b7; X[  9] = 32'hdbf67229; X[ 10] = 32'h975a5cfb; X[ 11] = 32'he8c35dca;
        X[ 12] = 32'h6abc86e8; X[ 13] = 32'hfee472cb; X[ 14] = 32'h83ac8f2e; X[ 15] = 32'h82825cff;
        X[ 16] = 32'h2d532c22; X[ 17] = 32'h2d3c3b06; X[ 18] = 32'he2862a8f; X[ 19] = 32'he8616ce4;
        X[ 20] = 32'h5d77ee51; X[ 21] = 32'he609de07; X[ 22] = 32'hef718044; X[ 23] = 32'h82f35f8b;
        X[ 24] = 32'hcdb9dcfe; X[ 25] = 32'hff6ea364; X[ 26] = 32'h0994ae28; X[ 27] = 32'h409b369b;
        X[ 28] = 32'hcfabda4e; X[ 29] = 32'h5cd52bbc; X[ 30] = 32'hd90e1715; X[ 31] = 32'h00f4dcf2;
        Y[  0] = 32'h01b2730a; Y[  1] = 32'h04ff1664; Y[  2] = 32'h6d55dc06; Y[  3] = 32'h1cda0da7;
        Y[  4] = 32'h98c812b4; Y[  5] = 32'ha8f79f3b; Y[  6] = 32'hb18d9ee1; Y[  7] = 32'ha53e97db;
        Y[  8] = 32'hfbbfd687; Y[  9] = 32'h6b8a8bf6; Y[ 10] = 32'h59fe5575; Y[ 11] = 32'he6ee62ca;
        Y[ 12] = 32'h9fe3f32a; Y[ 13] = 32'h6d758eaa; Y[ 14] = 32'h121e3dac; Y[ 15] = 32'h31d77884;
        Y[ 16] = 32'h8f2701dd; Y[ 17] = 32'hca5e7ac3; Y[ 18] = 32'h731977a3; Y[ 19] = 32'hc3c1af70;
        Y[ 20] = 32'h5606786a; Y[ 21] = 32'h94b71191; Y[ 22] = 32'hd044c7e2; Y[ 23] = 32'h7d899cd7;
        Y[ 24] = 32'hb17d4f5d; Y[ 25] = 32'h446e04de; Y[ 26] = 32'h9c40b33d; Y[ 27] = 32'habc2e23e;
        Y[ 28] = 32'hbb98b1f6; Y[ 29] = 32'hf1f87f7e; Y[ 30] = 32'hf19f3050; Y[ 31] = 32'h91305f4c;
        P[  0] = 32'h18bb0f97; P[  1] = 32'h08588a44; P[  2] = 32'h042c6647; P[  3] = 32'hc8d3fa09;
        P[  4] = 32'he2ddbbc7; P[  5] = 32'hef4a17fd; P[  6] = 32'h90c102ef; P[  7] = 32'h28b132db;
        P[  8] = 32'hebfd5f0a; P[  9] = 32'h958717ca; P[ 10] = 32'h563cd266; P[ 11] = 32'h433f41af;
        P[ 12] = 32'hbc198e83; P[ 13] = 32'h5b5300b2; P[ 14] = 32'h9bc50e5d; P[ 15] = 32'hefffa822;
        Q[  0] = 32'h25de0259; Q[  1] = 32'hd81461d0; Q[  2] = 32'h613815b3; Q[  3] = 32'h9bf274e0;
        Q[  4] = 32'hbfd89a48; Q[  5] = 32'hc53e71ac; Q[  6] = 32'hcce7aed3; Q[  7] = 32'hce1d017c;
        Q[  8] = 32'h646547e1; Q[  9] = 32'hd6779694; Q[ 10] = 32'h20ae39c0; Q[ 11] = 32'hef0d4b5b;
        Q[ 12] = 32'h8e5f59d6; Q[ 13] = 32'h7e267974; Q[ 14] = 32'h14c86644; Q[ 15] = 32'hed6efcd0;
        P_FACTOR[  0] = 32'h614f99ce; P_FACTOR[  1] = 32'hbcee5381; P_FACTOR[  2] = 32'h10b70a9a; P_FACTOR[  3] = 32'h1a697756;
        P_FACTOR[  4] = 32'h1a972b27; P_FACTOR[  5] = 32'hd7c43f9e; P_FACTOR[  6] = 32'h48cbad9c; P_FACTOR[  7] = 32'hc350e206;
        P_FACTOR[  8] = 32'h51098b50; P_FACTOR[  9] = 32'h93388ec6; P_FACTOR[ 10] = 32'h548960b5; P_FACTOR[ 11] = 32'h5ecd6b04;
        P_FACTOR[ 12] = 32'h04d1d543; P_FACTOR[ 13] = 32'ha53994af; P_FACTOR[ 14] = 32'hd390be70; P_FACTOR[ 15] = 32'h0acdced0;
        Q_FACTOR[  0] = 32'h8a19423f; Q_FACTOR[  1] = 32'h9d729c78; Q_FACTOR[  2] = 32'h26ed5239; Q_FACTOR[  3] = 32'h5a7eba92;
        Q_FACTOR[  4] = 32'h8465f60f; Q_FACTOR[  5] = 32'hd50817dd; Q_FACTOR[  6] = 32'hba703ab1; Q_FACTOR[  7] = 32'h3d59bd42;
        Q_FACTOR[  8] = 32'h2c197fcc; Q_FACTOR[  9] = 32'hed14f573; Q_FACTOR[ 10] = 32'hb860c105; Q_FACTOR[ 11] = 32'h89af91e7;
        Q_FACTOR[ 12] = 32'h13a3742d; Q_FACTOR[ 13] = 32'h96e41677; Q_FACTOR[ 14] = 32'h86506b4d; Q_FACTOR[ 15] = 32'h4a834535;
        P_COEFF[  0] = 32'hb3679fd9; P_COEFF[  1] = 32'hde24e467; P_COEFF[  2] = 32'hf0e82022; P_COEFF[  3] = 32'h99012919;
        P_COEFF[  4] = 32'h023bda43; P_COEFF[  5] = 32'hf04eebf8; P_COEFF[  6] = 32'h29e9942f; P_COEFF[  7] = 32'h8c257cb0;
        P_COEFF[  8] = 32'h5cdc4e60; P_COEFF[  9] = 32'h279bdff7; P_COEFF[ 10] = 32'hf04a610d; P_COEFF[ 11] = 32'h342901dc;
        P_COEFF[ 12] = 32'hc3e2f78c; P_COEFF[ 13] = 32'h39c00ed8; P_COEFF[ 14] = 32'hf7032ac2; P_COEFF[ 15] = 32'h22d9c54e;
        P_COEFF[ 16] = 32'h0000f994;
        Q_COEFF[  0] = 32'h95beda17; Q_COEFF[  1] = 32'ha4b101fa; Q_COEFF[  2] = 32'hd0b854bc; Q_COEFF[  3] = 32'h5c952a67;
        Q_COEFF[  4] = 32'h56722aa8; Q_COEFF[  5] = 32'h2176cace; Q_COEFF[  6] = 32'h69beef2d; Q_COEFF[  7] = 32'h95bf6eb2;
        Q_COEFF[  8] = 32'h0cf1175c; Q_COEFF[  9] = 32'h4911b74e; Q_COEFF[ 10] = 32'h331e61cb; Q_COEFF[ 11] = 32'he9527ead;
        Q_COEFF[ 12] = 32'h8d6a5911; Q_COEFF[ 13] = 32'hae42d654; Q_COEFF[ 14] = 32'he10d29a8; Q_COEFF[ 15] = 32'h50a5dd76;
        Q_COEFF[ 16] = 32'h0000ed75;
        DP[  0] = 32'h63d165e5; DP[  1] = 32'h856ac81e; DP[  2] = 32'hc4b8779d; DP[  3] = 32'h8b119544;
        DP[  4] = 32'had780837; DP[  5] = 32'h3e920266; DP[  6] = 32'he9d10f2e; DP[  7] = 32'h7c1b42b2;
        DP[  8] = 32'hc7daca3b; DP[  9] = 32'h7883be11; DP[ 10] = 32'ha384548d; DP[ 11] = 32'he0848b23;
        DP[ 12] = 32'h0b62bdff; DP[ 13] = 32'h11c64350; DP[ 14] = 32'h2aa1e225; DP[ 15] = 32'h9c2bcaa7;
        DQ[  0] = 32'hd7ffdc71; DQ[  1] = 32'hed01b8aa; DQ[  2] = 32'h2f99d3a6; DQ[  3] = 32'h8ccb4428;
        DQ[  4] = 32'hb1574616; DQ[  5] = 32'hfc218e36; DQ[  6] = 32'h4fe24f91; DQ[  7] = 32'h9c367c42;
        DQ[  8] = 32'h69dfa208; DQ[  9] = 32'h3ee3de79; DQ[ 10] = 32'h54ded59b; DQ[ 11] = 32'hcb3b487d;
        DQ[ 12] = 32'hbcc0db4e; DQ[ 13] = 32'hb3e6678c; DQ[ 14] = 32'h3d13ec03; DQ[ 15] = 32'h99e0f684;        QINV[  0] = 32'h9a2f0db2; QINV[  1] = 32'h4a8075a5; QINV[  2] = 32'hb61201fa; QINV[  3] = 32'h0e876a42;
        QINV[  4] = 32'h94667476; QINV[  5] = 32'h7538b796; QINV[  6] = 32'h8d8dfa35; QINV[  7] = 32'h689ee4a7;
        QINV[  8] = 32'h6779dd63; QINV[  9] = 32'he15b6b5e; QINV[ 10] = 32'h8275500c; QINV[ 11] = 32'he4dcd058;
        QINV[ 12] = 32'haf54b86c; QINV[ 13] = 32'hba76dc50; QINV[ 14] = 32'h473d0d6d; QINV[ 15] = 32'ha023ba44;
        XM[  0] = 32'h9b067dd2; XM[  1] = 32'hf47b497a; XM[  2] = 32'he8044305; XM[  3] = 32'hf74f1735;
        XM[  4] = 32'h494825f4; XM[  5] = 32'h077bf4a3; XM[  6] = 32'h637a9f36; XM[  7] = 32'h3c3821a2;
        XM[  8] = 32'haa1fe167; XM[  9] = 32'h01c7289a; XM[ 10] = 32'hb463d63d; XM[ 11] = 32'hc992252e;
        XM[ 12] = 32'he43762bf; XM[ 13] = 32'h351d9416; XM[ 14] = 32'h10e7f813; XM[ 15] = 32'h33187c87;
        XM[ 16] = 32'h9eb98306; XM[ 17] = 32'hb29be7b6; XM[ 18] = 32'h32b237a8; XM[ 19] = 32'h6c1d5e46;
        XM[ 20] = 32'h1cf10b4a; XM[ 21] = 32'hd874a710; XM[ 22] = 32'h7d2df198; XM[ 23] = 32'h463701cc;
        XM[ 24] = 32'h9b648da0; XM[ 25] = 32'hdc5d3b10; XM[ 26] = 32'hef88e7fd; XM[ 27] = 32'hcb888210;
        XM[ 28] = 32'h24397651; XM[ 29] = 32'h9b9bd5a2; XM[ 30] = 32'hbc796763; XM[ 31] = 32'h5be48377;
        YM[  0] = 32'h78aba2bd; YM[  1] = 32'h6885ed1d; YM[  2] = 32'h0d4983a2; YM[  3] = 32'h3b775d20;
        YM[  4] = 32'hf83145f4; YM[  5] = 32'h66e52536; YM[  6] = 32'h25c2377e; YM[  7] = 32'h91ef1342;
        YM[  8] = 32'h73013f57; YM[  9] = 32'h3862aa1a; YM[ 10] = 32'h37846437; YM[ 11] = 32'ha6ddd3c9;
        YM[ 12] = 32'h3974d1b2; YM[ 13] = 32'h02aea3f6; YM[ 14] = 32'h2e71b229; YM[ 15] = 32'hb898d5b6;
        YM[ 16] = 32'h71258bb8; YM[ 17] = 32'h654f94e8; YM[ 18] = 32'h5539e56e; YM[ 19] = 32'hd49567f2;
        YM[ 20] = 32'he73efaa1; YM[ 21] = 32'h3e4e2162; YM[ 22] = 32'h772d786a; YM[ 23] = 32'hc27be96a;
        YM[ 24] = 32'h9911c92d; YM[ 25] = 32'hddc1b0fd; YM[ 26] = 32'h829186bb; YM[ 27] = 32'h1bab454e;
        YM[ 28] = 32'h2f9fd9ce; YM[ 29] = 32'ha57103d4; YM[ 30] = 32'h1a93390c; YM[ 31] = 32'hc0376429;
        S[  0] = 32'h8eb4aa6e; S[  1] = 32'hababa077; S[  2] = 32'h8758f3f6; S[  3] = 32'h8282e4f4;
        S[  4] = 32'h747947ce; S[  5] = 32'h9ac7dbb0; S[  6] = 32'h9184f0b5; S[  7] = 32'h4b572f47;
        S[  8] = 32'hf4807458; S[  9] = 32'h6da8dcd4; S[ 10] = 32'h9f331c40; S[ 11] = 32'h65e2b7a2;
        S[ 12] = 32'hd3704e85; S[ 13] = 32'h3366f4f0; S[ 14] = 32'h035044b1; S[ 15] = 32'h54758bc4;
        S[ 16] = 32'h2a7e0970; S[ 17] = 32'hbcc7783c; S[ 18] = 32'hf62193e6; S[ 19] = 32'h5d7bb220;
        S[ 20] = 32'hb0fcabdd; S[ 21] = 32'he6dc5c88; S[ 22] = 32'h8e4d5e53; S[ 23] = 32'haa40acba;
        S[ 24] = 32'h1dfc9178; S[ 25] = 32'h842821bc; S[ 26] = 32'h318fc8e1; S[ 27] = 32'h0f8161fe;
        S[ 28] = 32'hbf3d7945; S[ 29] = 32'he33612c7; S[ 30] = 32'h7eec7f9d; S[ 31] = 32'h66da2c5a;
    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_BUS_FREQUENCY_MHZ   (50.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_bus = 1'b0;

    always #`CLK_PERIOD_HALF_NS clk = ~clk;
    
    always #`CLK_BUS_PERIOD_HALF_NS clk_bus = ~clk_bus;
    
    
    //
    // Reset
    //
    reg rst = 1'b1;
    
    
    //
    // 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_ready;
    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                (rst),
        
        .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)
    );


    //
    // Routine (Bus)
    //
    initial begin
    
        bus_ready = 1'b0;
    
        while (rst) wait_clock_bus_tick;
        wait_clock_bus_ticks(10);
        $display("Core came out of reset.");
         
        set_input_1;
        set_input_2;
        
        wait_clock_bus_ticks(10);
        bus_ready = 1'b1;
    
    end
    
    
    //
    // Routine (Control/Status, Bus)
    //
    initial begin
    
        _wait_half_clock_tick;
        wait_clock_ticks(100);
        rst = 1'b0;
        
        while (!bus_ready) wait_clock_tick;
        wait_clock_ticks(10);
        $display("Core input banks written.");
        
        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 = 9;//TB_MODULUS_LENGTH_N / 2 - 1;
        
        core_crt_mode      = 1'b1;
        
        core_next = 1'b1;
        wait_clock_tick;
        core_next = 1'b0;
        $display("Pulsed 'next' control signal.");
    
        while (!core_valid) wait_clock_tick;
        wait_clock_ticks(10);
        
        $display("Detected high 'valid' status signal.");
        core_crt_mode      = 1'bX;

        wait_clock_ticks(10);
        get_output;
        wait_clock_ticks(10);
        
        $display("Core output banks read.");
        
        //verify;
    
    end
    
    
    //
    // Variables
    //    
    integer _w, _n;
    

    //
    // set_input_1;
    //
    task 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'd0, BANK_IN_1_M,        _w[6:0],  M[_w]);
            for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_write(2'd0, BANK_IN_1_N,        _w[6:0],  N[_w]);
            for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_write(2'd0, 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'd0, BANK_IN_1_N_COEFF,  _w[6:0],  N_COEFF[_w]);
                                                   bus_write(2'd0, _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'd0, BANK_IN_1_X,        _w[6:0],  X[_w]);
            for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_write(2'd0, BANK_IN_1_Y,        _w[6:0],  Y[_w]);
        end
    endtask
    
    
    //
    // set_input_2;
    //
    task set_input_2;
        begin
//            for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_write(2'd0, BANK_IN_1_M,        _w[6:0], M[_w]);
            for (_w=0; _w< TB_NUM_WORDS_PQ; _w=_w+1) bus_write(2'd1, 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'd1, 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'd1, 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'd1, 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'd1, 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'd1, 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'd1, 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'd1, 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'd1, BANK_IN_2_QINV,     {      _w[6:0]}, QINV    [_w]);
        end
    endtask
    
    
    //
    // get_output;
    //
    task get_output;
        begin
            for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_read(2'd2, BANK_OUT_XM, _w[6:0], XM_READBACK[_w]);
            for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_read(2'd2, BANK_OUT_YM, _w[6:0], YM_READBACK[_w]);
            for (_w=0; _w<TB_NUM_WORDS_N; _w=_w+1) bus_read(2'd2, BANK_OUT_S,  _w[6:0], S_READBACK[_w]);
        end
    endtask


    //
    // verify;
    //
    task verify;
        //
        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_clock_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_clock_bus_tick;
            data = bus_data_rd;
            _bus_drive(1'b0, 1'b0, 12'hXXX, 32'hXXXXXXXX);
        end
    endtask
    
    
    //
    // _wait_half_clock_tick()
    //
    task _wait_half_clock_tick;
        #`CLK_PERIOD_HALF_NS;
    endtask
    
    //
    // wait_clock_tick()
    //
    task wait_clock_tick;
        begin
            _wait_half_clock_tick;
            _wait_half_clock_tick;
        end
    endtask
    
    
    //
    // wait_clock_bus_tick()
    //
    task wait_clock_bus_tick;
        #`CLK_BUS_PERIOD_NS;
    endtask


    //
    // wait_clock_ticks()
    //    
    task wait_clock_ticks;
        input integer num_ticks;
        for (_n=0; _n<num_ticks; _n=_n+1)
            wait_clock_tick;
    endtask
    
    
    //
    // wait_clock_bus_ticks()
    //    
    task wait_clock_bus_ticks;
        input integer num_ticks;
        for (_n=0; _n<num_ticks; _n=_n+1)
            wait_clock_bus_tick;
    endtask
    
endmodule