aboutsummaryrefslogblamecommitdiff
path: root/bench/tb_core_full_1024.v
blob: 90e3ae9b8525eabaa3374f6202448a0b4fd17269 (plain) (tree)
1
2
3

                    
                         





























                                                                        
                                     





















































































                                                                                                                               







                                                                                                   






                                                                                                       

                                                                                                               





























                                                                                                               





                                                             
                                                                
 
                                          


                                                                     












                                                                                                   
 
    









                                                          





                   
                      
















                                  


















                                                
                                    




















                                                 
                       
      








                                                  

      
             
      








                                         
 
        


                                                                                      
        


                                                                









                                                                                                                







                                                                                                                
                                      

           
    











































                                                                  








                   
                       
      
                          


                                                                                                               






                                                                                                                          




           
                       
      
                          
             









                                                                                                                                    




           
                      
      
                         
             




                                                                                                         




           
                         
      
                            
















































































                                                                                                        
                              














                                                                    
                              






                                                          
                               
      












                                   

           
 
      
                      
      
                       
             

                                




           
                          
      
                           




                            
                       
          
                        

                                         
                          



           
                           
          
                            

                                         
                              

           
 




























                                                                                                                            
         
`timescale 1ns / 1ps

module tb_core_full_1024;


    //
    // 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] 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'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;
        D[  0] = 32'h6d4e7831; D[  1] = 32'ha96f72d6; D[  2] = 32'h3b47788f; D[  3] = 32'h07207984;
        D[  4] = 32'h3247b99f; D[  5] = 32'hfe6ade84; D[  6] = 32'h0e0fdec4; D[  7] = 32'hc31450bd;
        D[  8] = 32'hb8624e9a; D[  9] = 32'h362966c6; D[ 10] = 32'h4d8a15bf; D[ 11] = 32'h845b6e48;
        D[ 12] = 32'h0b2259bf; D[ 13] = 32'h793bfff7; D[ 14] = 32'h28eeceb0; D[ 15] = 32'h8d6fde7f;
        D[ 16] = 32'h0d618632; D[ 17] = 32'hfb371e4b; D[ 18] = 32'h0b80a9a0; D[ 19] = 32'h5c1a1381;
        D[ 20] = 32'h8dfe755c; D[ 21] = 32'h82564b31; D[ 22] = 32'heee93cbd; D[ 23] = 32'h22bb686e;
        D[ 24] = 32'hf2ccae97; D[ 25] = 32'he3400736; D[ 26] = 32'h11c4a85e; D[ 27] = 32'ha0c904d0;
        D[ 28] = 32'hb495cecf; D[ 29] = 32'h32ceb818; D[ 30] = 32'ha2e46c28; D[ 31] = 32'h6915229e;
        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_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