Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/DEPS.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# DEPS configuration for Luhn Validator project

# Luhn validator testbench simulation
luhn_tb:
deps:
- luhn_validator.sv
- tb_luhn_validator.sv
top: tb_luhn_validator
Binary file added src/dumpfile_new.fst
Binary file not shown.
113 changes: 113 additions & 0 deletions src/luhn_validator.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Luhn Algorithm Validator
// Validates a 16-digit decimal number using the Luhn algorithm
// Input: 16 digits (each 4 bits, BCD encoded)
// Output: valid (1 if checksum passes, 0 otherwise)


module luhn_validator (
input logic [3:0] digit [15:0], // 16 digits, digit[0] is rightmost
output logic valid
);

// Processed digit values after doubling and digit sum
logic [3:0] processed [15:0];

// Total sum
logic [6:0] total_sum;

// Process each digit
// Digits at odd positions from right (digit[1], [3], [5], etc.) are doubled
// Digits at even positions from right (digit[0], [2], [4], etc.) stay the same

always_comb begin
// Process digit 0 (rightmost) - no doubling
processed[0] = digit[0];

// Process digit 1 - double it
if (digit[1] <= 4'd4) begin
processed[1] = digit[1] << 1; // Just double
end else begin
processed[1] = (digit[1] << 1) - 4'd9; // Double and subtract 9 (adds digits)
end

// Process digit 2 - no doubling
processed[2] = digit[2];

// Process digit 3 - double it
if (digit[3] <= 4'd4) begin
processed[3] = digit[3] << 1;
end else begin
processed[3] = (digit[3] << 1) - 4'd9;
end

// Process digit 4 - no doubling
processed[4] = digit[4];

// Process digit 5 - double it
if (digit[5] <= 4'd4) begin
processed[5] = digit[5] << 1;
end else begin
processed[5] = (digit[5] << 1) - 4'd9;
end

// Process digit 6 - no doubling
processed[6] = digit[6];

// Process digit 7 - double it
if (digit[7] <= 4'd4) begin
processed[7] = digit[7] << 1;
end else begin
processed[7] = (digit[7] << 1) - 4'd9;
end

// Process digit 8 - no doubling
processed[8] = digit[8];

// Process digit 9 - double it
if (digit[9] <= 4'd4) begin
processed[9] = digit[9] << 1;
end else begin
processed[9] = (digit[9] << 1) - 4'd9;
end

// Process digit 10 - no doubling
processed[10] = digit[10];

// Process digit 11 - double it
if (digit[11] <= 4'd4) begin
processed[11] = digit[11] << 1;
end else begin
processed[11] = (digit[11] << 1) - 4'd9;
end

// Process digit 12 - no doubling
processed[12] = digit[12];

// Process digit 13 - double it
if (digit[13] <= 4'd4) begin
processed[13] = digit[13] << 1;
end else begin
processed[13] = (digit[13] << 1) - 4'd9;
end

// Process digit 14 - no doubling
processed[14] = digit[14];

// Process digit 15 - double it
if (digit[15] <= 4'd4) begin
processed[15] = digit[15] << 1;
end else begin
processed[15] = (digit[15] << 1) - 4'd9;
end

// Calculate total sum
total_sum = processed[0] + processed[1] + processed[2] + processed[3] +
processed[4] + processed[5] + processed[6] + processed[7] +
processed[8] + processed[9] + processed[10] + processed[11] +
processed[12] + processed[13] + processed[14] + processed[15];

// Valid if sum modulo 10 equals 0 (last digit is 0)
valid = (total_sum % 7'd10) == 7'd0;
end

endmodule
167 changes: 167 additions & 0 deletions src/pan_stream.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// Module 1: PAN input stream: 16 digits
// What it does:
// - Receives a stream of digits (0..9) with digit_valid
// - start = 1-cycle pulse to begin a new PAN
// - end = 1-cycle pulse to finish the PAN (ideally asserted with the last digit_valid)
// - Counts digits (length), captures IIN prefix (first 8 digits), and streams digits out
//
// Outputs:
// - card_done (pulse when end received)
// - len_final and length_ok
// - iin_prefix and iin_ready
// - error_flag if protocol is weird (e.g., end without digit_valid)

module pan_stream #(
parameter int MIN_LEN = 13,
parameter int MAX_LEN = 19,
parameter int IIN_DIGITS = 6 // capture first 6 digits
) (
input logic clk,
input logic rst_n,

// Host inputs
input logic start, // 1-cycle pulse: begin new card
input logic pan_end, // 1-cycle pulse: last digit has been sent
input logic digit_valid,
input logic [3:0] digit_in, //actual digit/bit value being streamed in
input logic abort, // optional: cancel card (can tie low)

// Stream outputs (to Luhn)
output logic [3:0] s_digit, //actual digit/bit value being streamed onward
output logic s_valid, //high when module is accepting digits
output logic s_first, // pulse on first accepted digit
output logic s_last, // pulse on last accepted digit

// Length outputs (how many have been recieved/acceptable)
output logic [4:0] len_count, // running count
output logic [4:0] len_final, // how many numbers counter between 13 - 19
output logic len_parity, // parity of final length odd or even: needed for luhn alogrithm
output logic length_ok, // valid length range?

// IIN capture (to issuer lookup)
output logic [31:0] iin_prefix, // 8 digits packed as nibbles
output logic [3:0] iin_digits_captured, // 0..8
output logic iin_ready, // high once enough digits captured

// Framing / status
output logic in_progress,
output logic card_done, // 1-cycle pulse when card finishes
output logic digit_ok, // sticky: all digits were 0..9
output logic error_flag // sticky: protocol / invalid digit errors
);

// Accept digits only while in_progress
//logic replaces reg/wire: is simpler because can be used in alwyas block
logic accept_digit;
assign accept_digit = in_progress && digit_valid;

// Forward digit stream (gated)
//sv version of always@(*) combinational block
always_comb begin
s_digit = digit_in;
s_valid = accept_digit;
end

// Sequential state
//updated ff on every pos edge
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
//reset states
in_progress <= 1'b0;
len_count <= 5'd0;
len_final <= 5'd0;

iin_prefix <= 32'd0;
iin_digits_captured <= 4'd0;
iin_ready <= 1'b0;

s_first <= 1'b0;
s_last <= 1'b0;
card_done <= 1'b0;

digit_ok <= 1'b1;
error_flag <= 1'b0;
end

else begin
// default pulses low each cycle
s_first <= 1'b0;
s_last <= 1'b0;
card_done <= 1'b0;

// Start a new PAN
//set to 0 at start of every clk, when digit is sent, set pulse to 1
if (start) begin
in_progress <= 1'b1; //actively accepting digits
len_count <= 5'd0; //start count from 0
len_final <= 5'd0;

iin_prefix <= 32'd0; //clear IIN
iin_digits_captured <= 4'd0;
iin_ready <= 1'b0;

digit_ok <= 1'b1; //assume digit ok until error
error_flag <= 1'b0; // clear errors for new card
end

// Abort cancels the current PAN
if (abort && in_progress) begin
in_progress <= 1'b0;
error_flag <= 1'b1;
end

// Accept a digit
if (accept_digit) begin
// First digit pulse
if (len_count == 5'd0) begin
s_first <= 1'b1;
end

// Digit validity check
if (digit_in > 4'd9) begin
digit_ok <= 1'b0;
error_flag <= 1'b1;
end

// Increment length
len_count <= len_count + 5'd1;

// Capture first IIN_DIGITS digits into iin_prefix
if (iin_digits_captured < IIN_DIGITS[3:0]) begin
iin_prefix <= (iin_prefix & ~(32'hF << (4*iin_digits_captured))) |
({28'd0, digit_in} << (4*iin_digits_captured));
iin_digits_captured <= iin_digits_captured + 4'd1;

// Mark ready after at least 6 digits (adjust if you want 8)
if (iin_digits_captured + 4'd1 >= 4'd6)
iin_ready <= 1'b1;
end
end

// End-of-card handling
// Assumption: end is asserted on the same cycle as the last digit_valid.
if (pan_end && in_progress) begin
if (digit_valid) begin
s_last <= 1'b1;
end else begin
// end without digit_valid is odd; flag it
error_flag <= 1'b1;
end

// Latch final length:
// If end coincides with digit_valid, final length should include that digit.
len_final <= len_count + (digit_valid ? 5'd1 : 5'd0);

card_done <= 1'b1;
in_progress <= 1'b0;
end
end
end

// Derived outputs
always_comb begin
len_parity = len_final[0];
length_ok = (len_final >= MIN_LEN[4:0]) && (len_final <= MAX_LEN[4:0]);
end

endmodule
Loading