// tb_sample_ntt.cpp - Verilator C++ testbench for sample_ntt_sync // // Reads test vectors from +VECTOR_FILE= plusarg. // Each line: "RHO_HEX K_HEX I_HEX J_HEX" // RHO_HEX: 64 hex chars (32 bytes, MSB-first per byte pair) // K_HEX, I_HEX, J_HEX: single hex digits // // Drives DUT, waits for 256 coefficients via valid_o handshake, // prints "RESULT: CCC" for each (12-bit hex). // // Clock: 10ns period. Reset: 2 cycles. Timeout: 2,000,000 cycles. #include #include #include #include #include #include #include #include "Vsample_ntt_sync.h" #include "verilated.h" #define CLK_PERIOD_NS 10.0 #define TIMEOUT_CYCLES 2000000 #define Q 3329 #define N_COEFFS 256 static vluint64_t main_time = 0; double sc_time_stamp() { return main_time; } static void posedge(Vsample_ntt_sync* dut) { dut->clk = !dut->clk; main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0); dut->eval(); dut->clk = !dut->clk; main_time += (vluint64_t)(CLK_PERIOD_NS / 2.0); dut->eval(); } static int hex_char_to_nibble(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'A' && c <= 'F') return c - 'A' + 10; if (c >= 'a' && c <= 'f') return c - 'a' + 10; return 0; } static void parse_rho(const std::string& hex, Vsample_ntt_sync* dut) { for (int w = 0; w < 8; w++) dut->rho_i[w] = 0; int byte_count = 0; for (size_t i = 0; i < hex.length() && byte_count < 32; i += 2) { while (i < hex.length() && (hex[i] == ' ' || hex[i] == '\t')) i++; if (i + 1 >= hex.length()) break; char high_c = hex[i]; char low_c = hex[i + 1]; uint8_t byte_val = (hex_char_to_nibble(high_c) << 4) | hex_char_to_nibble(low_c); int word_idx = byte_count / 4; int byte_off = byte_count % 4; if (word_idx < 8) { dut->rho_i[word_idx] |= ((uint32_t)byte_val << (byte_off * 8)); } byte_count++; } } int main(int argc, char** argv) { Verilated::commandArgs(argc, argv); const char* vector_file = NULL; for (int i = 1; i < argc; i++) { std::string arg(argv[i]); if (arg.rfind("+VECTOR_FILE=", 0) == 0) { vector_file = argv[i] + 13; } } if (!vector_file) { std::cerr << "ERROR: +VECTOR_FILE= not specified" << std::endl; return 1; } std::ifstream infile(vector_file); if (!infile.is_open()) { std::cerr << "ERROR: Cannot open vector file: " << vector_file << std::endl; return 1; } Vsample_ntt_sync* dut = new Vsample_ntt_sync; // Initialize dut->clk = 0; dut->rst_n = 0; for (int i = 0; i < 8; i++) dut->rho_i[i] = 0; dut->k_i = 0; dut->i_idx = 0; dut->j_idx = 0; dut->valid_i = 0; dut->ready_i = 0; // Reset: 2 full cycles for (int i = 0; i < 2; i++) posedge(dut); dut->rst_n = 1; dut->ready_i = 1; std::string line; vluint64_t cycle = 0; int vec_count = 0; while (std::getline(infile, line)) { if (line.empty() || line[0] == '#') continue; std::string rho_hex, k_str, i_str, j_str; std::istringstream iss(line); if (!(iss >> rho_hex >> k_str >> i_str >> j_str)) { std::cerr << "ERROR: Malformed input line: " << line << std::endl; continue; } if (rho_hex.length() < 64) { std::cerr << "ERROR: RHO_HEX too short" << std::endl; continue; } int k_val = 0, i_val = 0, j_val = 0; if (!k_str.empty()) k_val = hex_char_to_nibble(k_str[0]); if (!i_str.empty()) i_val = hex_char_to_nibble(i_str[0]); if (!j_str.empty()) j_val = hex_char_to_nibble(j_str[0]); // Set inputs parse_rho(rho_hex, dut); dut->k_i = k_val & 0x7; dut->i_idx = i_val & 0x3; dut->j_idx = j_val & 0x3; dut->valid_i = 1; // Wait for ready_o (DUT must be IDLE) while (!dut->ready_o && cycle < TIMEOUT_CYCLES) { posedge(dut); cycle++; } if (cycle >= TIMEOUT_CYCLES) { std::cerr << "ERROR: Timeout waiting for ready_o (vec " << vec_count << ")" << std::endl; goto done; } // Capture edge posedge(dut); cycle++; dut->valid_i = 0; // Wait for and print 256 coefficients int coeff_count = 0; while (coeff_count < N_COEFFS && cycle < TIMEOUT_CYCLES) { while (!dut->valid_o && cycle < TIMEOUT_CYCLES) { posedge(dut); cycle++; } if (cycle >= TIMEOUT_CYCLES) { std::cerr << "ERROR: Timeout waiting for coeff " << coeff_count << " (vec " << vec_count << ")" << std::endl; goto done; } uint32_t coeff_val = (uint32_t)(dut->coeff_o) & 0xFFF; printf("RESULT: %03X\n", coeff_val); posedge(dut); cycle++; coeff_count++; } vec_count++; } done: infile.close(); delete dut; if (vec_count == 0) { std::cerr << "ERROR: No vectors processed" << std::endl; return 1; } return 0; }