0% found this document useful (0 votes)
3 views32 pages

Exlpamtion of Code

The document provides a detailed explanation of three modules in a UART communication system: frame_detector, command_buffer, and command_fsm. The frame_detector identifies the start of a UART frame, the command_buffer collects subsequent bytes, and the command_fsm processes commands based on the collected data. Each module's purpose, signal definitions, internal logic, and interactions with other modules are thoroughly described.

Uploaded by

ganniyadav9392
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views32 pages

Exlpamtion of Code

The document provides a detailed explanation of three modules in a UART communication system: frame_detector, command_buffer, and command_fsm. The frame_detector identifies the start of a UART frame, the command_buffer collects subsequent bytes, and the command_fsm processes commands based on the collected data. Each module's purpose, signal definitions, internal logic, and interactions with other modules are thoroughly described.

Uploaded by

ganniyadav9392
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 32

EXLPAMTION OF CODE:

✅ MODULE 1: frame_detector.v

📘 Purpose:

Detects when a new valid UART frame starts by looking for a special byte (FRAME_ID =
0xA5). It then sends a one-cycle pulse (frame_start) to trigger the command buffer.

🧠 Line-by-Line with Signal Explanation:

`timescale 1ns / 1ps

Defines simulation timing:

• 1ns = time unit

• 1ps = resolution (accuracy of delay)

module frame_detector (

input wire clk, // Main clock input

input wire rst, // Active-high reset

input wire [7:0] rx_data, // 8-bit UART received data

input wire rx_data_valid, // 1-bit signal; high when rx_data is valid

output reg frame_start // 1-bit output; pulses high when frame ID is detected

);

📌 Signal Details:

Signal Width Direction Purpose

clk 1-bit input Synchronizes the logic (drives everything)

rst 1-bit input When high, resets all registers

rx_data 8-bit input UART received byte

rx_data_valid 1-bit input Pulses high to indicate rx_data is new

frame_start 1-bit output Pulses high for 1 cycle when frame ID is detected
parameter FRAME_ID = 8'hA5;

Defines the expected frame identifier byte. If rx_data equals this, then it's the start of a
command.

reg rx_data_valid_d;

🧠 Holds the previous value of rx_data_valid — used for rising edge detection.

wire rx_data_valid_rising = rx_data_valid & ~rx_data_valid_d;

Creates a wire that becomes high (1) only when rx_data_valid just transitioned from 0 → 1.

📘 Why? This ensures that you only act once per new byte, even if rx_data_valid stays high
for multiple cycles.

🌀 Always Block 1 — Delaying rx_data_valid

always @(posedge clk or posedge rst) begin

Triggers on every clock cycle or when reset is asserted.

if (rst) begin

rx_data_valid_d <= 0;

end else begin

rx_data_valid_d <= rx_data_valid;

end

✅ On reset, clear the delayed signal.


✅ On every clock, save the current rx_data_valid into rx_data_valid_d so it can be used
next time.

🌀 Always Block 2 — Detecting the Frame ID

always @(posedge clk or posedge rst) begin

if (rst) begin

frame_start <= 0;
end else begin

Start logic: On reset, make sure frame_start is 0.

if (rx_data_valid && rx_data == FRAME_ID) begin

frame_start <= 1;

$display("[FRAME DETECTOR] Frame ID (0x%h) detected!", FRAME_ID);

end else begin

frame_start <= 0;

end

🔍 Logic Breakdown:

• If a valid byte is received and that byte is equal to 0xA5, then:

o Set frame_start = 1 (pulse)

o Show a debug message (visible during simulation)

📌 frame_start goes high for only one clock cycle.

🧩 How It Connects to Other Modules:

• Output frame_start goes to the command_buffer.

• It tells the buffer: “Start storing the next 4 bytes coming from UART!”

✅ Summary of Variable Roles

Variable / Wire Type Purpose

clk Input Drives the logic

rst Input Resets the module

rx_data Input Byte received from UART

rx_data_valid Input Tells us when a new byte arrives

frame_start Output Pulses high when frame ID (0xA5) is seen


Variable / Wire Type Purpose

FRAME_ID Param Special byte used to detect start of frame

rx_data_valid_d Reg Stores previous rx_data_valid

rx_data_valid_rising Wire Becomes high only on the 0→1 edge of rx_data_valid

✅ MODULE 2: command_buffer.v

📘 Purpose:

After frame_start is detected by the frame_detector, this module collects the next 4 bytes
received from the UART:

1. Command MSB

2. Command LSB

3. Data MSB

4. Data LSB

Once all 4 are received, it sets buffer_full = 1 to tell the FSM to start processing the
command.

🧠 Signal-by-Signal Definitions:

module command_buffer (

input wire clk, // Clock

input wire rst, // Reset

input wire [7:0] rx_data, // Received byte from UART

input wire rx_data_valid, // High for 1 cycle when rx_data is valid

input wire frame_start, // High for 1 cycle when 0xA5 is detected

output reg [7:0] cmd_msb, // First command byte (most significant)

output reg [7:0] cmd_lsb, // Second command byte (least significant)

output reg [7:0] data_msb, // First data byte (most significant)

output reg [7:0] data_lsb, // Second data byte (least significant)


output reg buffer_full // High when all 4 bytes are captured

);

📌 Signal Purpose Table:

Signal Width Direction Description

clk 1-bit input System clock

rst 1-bit input Resets the buffer to initial state

rx_data 8-bit input Incoming UART byte

rx_data_valid 1-bit input Pulses high when rx_data is ready

frame_start 1-bit input Comes from frame_detector; triggers new buffer cycle

cmd_msb 8-bit output First command byte (address or operation)

cmd_lsb 8-bit output Second command byte (address or extra info)

data_msb 8-bit output First data byte

data_lsb 8-bit output Second data byte

buffer_full 1-bit output Signals that 4 bytes have been stored

🧠 Internal Registers and States

reg [2:0] byte_cnt;

🧮 Byte counter: Tracks how many of the 4 bytes we’ve received (0 → 3). Needs 3 bits
because max value = 3.

reg [1:0] process_state;

🧠 State of the FSM inside the buffer.

parameter IDLE = 2'b00,

FILLING = 2'b01,

FULL = 2'b10,

PROCESSING = 2'b11;
📖 State Meaning:

State Description

IDLE Waiting for frame start (from frame_detector)

FILLING Receiving the 4 data bytes

FULL All 4 bytes received, waiting for FSM to process

PROCESSING FSM has read the values, reset soon

🌀 Main Always Block: FSM and Storage

always @(posedge clk or posedge rst)

Trigger this block on:

• Positive edge of clk, or

• Rising edge of rst (reset active)

1. RESET: Initialize all registers

if (rst) begin

byte_cnt <= 0;

buffer_full <= 0;

cmd_msb <= 8'h00;

cmd_lsb <= 8'h00;

data_msb <= 8'h00;

data_lsb <= 8'h00;

process_state <= IDLE;

end

🔄 All variables are reset:

• byte_cnt set to 0

• Each byte register cleared to 0

• buffer_full lowered
• FSM goes to IDLE

2. FSM Behavior (non-reset)

else begin

case (process_state)

📘 Case: IDLE

if (frame_start) begin

byte_cnt <= 0;

buffer_full <= 0;

process_state <= FILLING;

$display("[CMD_BUFFER] New frame detected, started filling buffer");

end

💡 Logic:

• If a new frame is detected (frame_start = 1)

• Reset byte_cnt, clear buffer_full

• Start capturing bytes

📘 Case: FILLING

if (rx_data_valid) begin

byte_cnt <= byte_cnt + 1;

⬇ For each value of byte_cnt, assign incoming data:

case (byte_cnt)

0: begin

cmd_msb <= rx_data;

$display("[CMD_BUFFER] Received Command MSB: 0x%h", rx_data);

end

1: begin
cmd_lsb <= rx_data;

$display("[CMD_BUFFER] Received Command LSB: 0x%h", rx_data);

end

2: begin

data_msb <= rx_data;

$display("[CMD_BUFFER] Received Data MSB: 0x%h", rx_data);

end

3: begin

data_lsb <= rx_data;

buffer_full <= 1;

process_state <= FULL;

$display("[CMD_BUFFER] Received Data LSB: 0x%h, buffer now FULL", rx_data);

end

endcase

📌 Summary:

• Store the byte in order

• On the 4th byte (count = 3), buffer is full, move to FULL

🛡 Extra check: Restart buffering if a new frame_start is detected mid-fill

if (frame_start) begin

byte_cnt <= 0;

$display("[CMD_BUFFER] New frame detected while filling, reset byte counter");

end

📘 Case: FULL

process_state <= PROCESSING;

$display("[CMD_BUFFER] Command is being processed");

✅ Transition to PROCESSING state, indicating FSM should now use the data.
📘 Case: PROCESSING

buffer_full <= 0;

process_state <= IDLE;

$display("[CMD_BUFFER] Command processing complete, clearing buffer_full");

✅ Reset buffer for next cycle once FSM is done reading.

✅ Summary Table of All Internal Variables

Variable Type Width Role

byte_cnt reg 3 Tracks how many bytes have been received

process_state reg 2 FSM state (IDLE, FILLING, etc.)

cmd_msb reg 8 First command byte

cmd_lsb reg 8 Second command byte

data_msb reg 8 First data byte

data_lsb reg 8 Second data byte

buffer_full reg 1 Flag: High when all 4 bytes are ready

🔗 Interaction with Other Modules

• Input: frame_start from frame_detector

• Input: rx_data, rx_data_valid from UART RX

• Output: All 4 command+data bytes → command_fsm

• Output: buffer_full signals FSM to start execution

✅ MODULE 3: command_fsm.v

📘 Purpose:
This is the command decoder and executor. Once command_buffer sets buffer_full = 1, this
module reads the command and data, decides if it’s a read or write, and:

• For WRITE: updates a register in register_file.

• For READ: fetches data and sends it back using UART transmitter (tx_data, tx_start).

🧠 Inputs/Outputs and Signal Roles:

module command_fsm (

input wire clk, // Clock

input wire rst, // Reset

input wire buffer_full, // High when command_buffer has 4 bytes ready

input wire [7:0] cmd_msb, // First command byte (MSB)

input wire [7:0] cmd_lsb, // Second command byte (LSB)

input wire [7:0] data_msb, // First data byte (MSB)

input wire [7:0] data_lsb, // Second data byte (LSB)

input wire tx_busy, // UART TX is busy sending a byte

input wire [15:0] reg_data_out, // Output from register_file after a READ

output reg [3:0] reg_addr, // Address to read/write in register_file

output reg [15:0] reg_data_in,// Data to write into register_file

output reg write_en, // Enable signal for writing

output reg read_en, // Enable signal for reading

output reg tx_start, // Starts UART TX when high

output reg [7:0] tx_data // Byte to transmit over UART

);

📌 Signal Purpose Table


Signal Width Direction Purpose

clk, rst 1 input Control timing and reset

buffer_full 1 input Signals new command is ready

cmd_msb, cmd_lsb 8 input 16-bit command (address, opcode)

data_msb, data_lsb 8 input 16-bit data to be written

tx_busy 1 input From UART TX — high when sending a byte

reg_data_out 16 input Output from register_file on read

reg_addr 4 output Register address for read/write

reg_data_in 16 output Data to write into register

write_en 1 output Active high = write operation

read_en 1 output Active high = read operation

tx_start 1 output Starts UART TX when high

tx_data 8 output Byte to send via UART

🧠 Internal Registers

parameter IDLE = 3'b000,

EXECUTE = 3'b001,

WAIT_READ = 3'b010,

PREPARE_HIGH = 3'b011,

SEND_HIGH = 3'b100,

WAIT_TX1 = 3'b101,

PREPARE_LOW = 3'b110,

SEND_LOW = 3'b111,

WAIT_TX2 = 4'b1000;

📘 These are states in the FSM. They define the exact steps in the command flow:
State Meaning

IDLE Wait for new command

EXECUTE Decide READ or WRITE

WAIT_READ Give time for register_file to produce output

PREPARE_HIGH Prepare MSB of read data

SEND_HIGH Send MSB byte to UART

WAIT_TX1 Wait while UART sends

PREPARE_LOW Prepare LSB of read data

SEND_LOW Send LSB byte

WAIT_TX2 Wait until UART is done

reg [3:0] state, next_state;

🧠 Current and next state for FSM.

reg buffer_full_d;

wire buffer_full_rising = buffer_full & ~buffer_full_d;

Edge detection logic to trigger FSM only on new buffer_full rising.

reg [15:0] read_data_reg;

Stores register output temporarily so it won’t change while sending over UART.

wire [15:0] full_data = {data_msb, data_lsb};

wire [15:0] full_cmd = {cmd_msb, cmd_lsb};

Combines two 8-bit halves into full 16-bit values.

🌀 Always Block 1: Edge detection and initialization

always @(posedge clk or posedge rst)

Triggered every clock or reset.


if (rst) begin

buffer_full_d <= 0;

reg_addr <= 4'b0000;

end else begin

buffer_full_d <= buffer_full;

end

Keeps a delayed version of buffer_full. Used to detect new data arrival.

🌀 Always Block 2: State transitions and data updates

always @(posedge clk or posedge rst)

if (rst) begin

state <= IDLE;

read_data_reg <= 16'd0;

tx_data <= 8'd0;

reg_addr <= 4'b0000;

reg_data_in <= 16'h0000;

end else begin

state <= next_state;

if (buffer_full_rising) begin

reg_addr <= full_cmd[3:0];

reg_data_in <= full_data;

end

if (state == WAIT_READ) begin

read_data_reg <= reg_data_out;

end
if (state == PREPARE_HIGH)

tx_data <= read_data_reg[15:8];

if (state == PREPARE_LOW)

tx_data <= read_data_reg[7:0];

end

📘 Logic:

• If buffer becomes full, extract the lower 4 bits of command as register address.

• Store data to be written.

• Latch read data into a register so we can safely transmit it.

• Prepare the TX data byte-by-byte.

🌀 Always Block 3: Debug prints

reg [3:0] prev_state;

always @(posedge clk) begin

prev_state <= state;

if (prev_state != state) begin

...

end

end

Only prints state transitions during simulation for debug purposes.

🧠 Always Block 4: Combinational Next-State + Control Logic

always @(*) begin

next_state = state;

write_en = 0;

read_en = 0;

tx_start = 0;
📘 For every state, we decide:

• What should the next state be?

• Should we trigger read_en, write_en, or tx_start?

🔁 State Logic Breakdown:

🧩 IDLE → EXECUTE

if (buffer_full_rising)

next_state = EXECUTE;

🧩 EXECUTE

if (full_cmd[15:8] == 8'h01) begin

write_en = 1;

next_state = IDLE;

end else if (full_cmd[15:8] == 8'h02) begin

read_en = 1;

next_state = WAIT_READ;

end else

next_state = IDLE;

Decodes command:

• 0x01: Write command

• 0x02: Read command

🧩 WAIT_READ → PREPARE_HIGH

Delay for read

🧩 PREPARE_HIGH → SEND_HIGH

Load upper 8 bits of data

🧩 SEND_HIGH

If UART not busy:


tx_start = 1;

next_state = WAIT_TX1;

🧩 WAIT_TX1

Wait for UART to finish

🧩 PREPARE_LOW → SEND_LOW

Load lower 8 bits of data

🧩 SEND_LOW

Same logic as above: Start TX if not busy

🧩 WAIT_TX2 → IDLE

Once UART is idle, we go back to wait for new command

✅ Variable Summary Table

Name Type Role

state, next_state reg Tracks FSM position

read_data_reg reg Stores read output temporarily

buffer_full_d reg Used to detect new rising edge of buffer_full

tx_data reg Byte to be sent over UART

reg_data_in reg Data to write into register file

reg_addr reg Register file address (4 bits = 16 registers)

🔗 Module Connections

• Inputs from: command_buffer, register_file, UART TX

• Outputs to:

o Register file (read_en, write_en, reg_addr, reg_data_in)

o UART TX (tx_start, tx_data)


Great! Let's now break down the final module in the same super-detailed, beginner-friendly
format:

✅ MODULE 4: register_file.v

📘 Purpose:

This module is a 16x16-bit register bank. It allows:

• Writing 16-bit data into one of 16 registers (regs[0] to regs[15])

• Reading 16-bit data out of any register It’s controlled by read_en, write_en, and the
addr signal.

🧠 Inputs and Outputs

module register_file (

input wire clk, // System clock

input wire rst, // Asynchronous reset (active high)

input wire [3:0] addr, // Address of the register (4 bits = 16 addresses)

input wire [15:0] data_in, // Data to be written into register

input wire write_en, // Write enable signal (active high)

input wire read_en, // Read enable signal (active high)

output reg [15:0] data_out // Data output from selected register

);

📌 Signal Purpose Table

Signal Width Dir Purpose

clk 1 input Main timing source

rst 1 input Reset clears all registers

addr 4 input Selects which of 16 registers to read/write

data_in 16 input Data to be stored

write_en 1 input High = write data_in to register


Signal Width Dir Purpose

read_en 1 input High = load register value into data_out

data_out 16 output Contains the read value (if read_en is high)

🧠 Internal Registers

reg [15:0] regs[0:15];

🧠 This is a memory array with 16 registers, each 16-bits wide.

• regs[0] to regs[15] store values written from UART commands.

integer i;

Used for looping during reset to initialize all registers.

🌀 Main Register Logic

always @(posedge clk or posedge rst)

The register file updates on the rising edge of the clock or during reset.

🧩 RESET LOGIC

if (rst) begin

for (i = 0; i < 16; i = i + 1)

regs[i] <= 16'h0000;

data_out <= 16'h0000;

end

📘 What this does:

• Loops through all 16 registers (i = 0 to 15)

• Sets every register to 0x0000

• Also clears the data_out output register


🧩 WRITE Operation

else begin

if (write_en) begin

regs[addr] <= data_in;

$display("[REGFILE] Writing reg[%0d] <= 0x%h", addr, data_in);

end

📘 Logic:

• If write_en is high,

o Write the data_in into the selected register (regs[addr])

o Example: if addr = 4'b0011 and data_in = 0xDEAD, this line writes 0xDEAD
into regs[3]

🧪 A debug message is printed to show what’s being written.

🧩 READ Operation

if (read_en) begin

data_out <= regs[addr];

$display("[REGFILE] Reading reg[%0d] => 0x%h (setting data_out)", addr, regs[addr]);

end

end

📘 Logic:

• If read_en is high:

o Set data_out to the content of regs[addr]

o This makes the value available to command_fsm, which will then send it via
UART

🧩 Debug Monitor

always @(data_out) begin

$display("[REGFILE DEBUG] data_out changed to 0x%h", data_out);


end

🧪 This block triggers whenever data_out changes and prints the new value. Very helpful
for:

• Debugging during simulation

• Verifying that a read operation produced the correct output

✅ Variable Summary Table

Variable Type Width Purpose

regs reg array 16x16 Storage bank: 16 registers, 16 bits each

i integer - Used in reset loop

addr input 4 Selects register to read/write

data_in input 16 Data to write into selected register

write_en input 1 Write trigger

read_en input 1 Read trigger

data_out output reg 16 Output of register file (selected value)

🔗 Module Connections

Connected To Signal Flow

command_fsm Sends: reg_addr, reg_data_in, write_en, read_en

register_file Returns: reg_data_out to FSM

🔁 Example Flow:

▶ Write:

• FSM issues:

o reg_addr = 4'h2

o reg_data_in = 0xBEEF
o write_en = 1

• Result: regs[2] <= 16'hBEEF

▶ Read:

• FSM issues:

o reg_addr = 4'h2

o read_en = 1

• Result: data_out = regs[2] = 0xBEEF → FSM sends this via UART

✅ That completes all four modules! Summary:

Module Role

frame_detector Detects start of a new command

command_buffer Stores command and data bytes

command_fsm Decodes command and performs read/write

register_file Stores and returns 16-bit values


UART TRASMITTER EXPLANATION

✅ MODULE 1: UART_TX.v

🔷 Purpose:

This is the top-level UART Transmitter module that:

• Receives a byte (8 bits) to be sent.

• Breaks it into a UART frame: start bit + data bits + parity + stop bit.

• Sends it serially, bit by bit, on the TX_data_out line.

• Uses four submodules: TX_PISO, TX_PARITY, TX_FSM, and TX_MUX.

Inputs & Outputs:

input clk, // Clock signal (synchronizes everything)

input rst, // Reset (clears all logic to initial state)

input TX_start, // Goes high for 1 cycle to begin a transmission

input [7:0] TX_data_in, // The 8-bit data to transmit

output TX_data_out, // The UART serial output (bit stream goes out here)

output TX_busy // HIGH while transmission is happening

Internal Wires and Registers:

wire piso_out; // Output bit from PISO shift register

wire [1:0] mux_sel; // Selects which UART bit to send (start/data/parity/stop)

wire piso_load, piso_shift; // Controls for loading or shifting the PISO

wire parity_bit, parity_load; // Output and trigger for parity generator

reg [7:0] latched_data; // Stores input byte for duration of transmission

reg TX_start_d; // Delayed version of TX_start for edge detection

What is “Edge Detection”?


wire TX_start_rising = TX_start & ~TX_start_d;

This logic detects when TX_start goes from 0 → 1. We only want to start a new
transmission on the rising edge of this signal.

Latching Previous TX_start (for edge detection)

always @(posedge clk or posedge rst) begin

if (rst)

TX_start_d <= 0;

else

TX_start_d <= TX_start;

end

Stores TX_start every clock to compare with current value (for edge detection above).

Debug Logging on TX_start

always @(posedge clk) begin

if (TX_start_rising)

$display("[UART_TX] TX_start rising edge detected, data = 0x%h", TX_data_in);

end

This prints when a transmission is starting, so you can track what's being sent during
simulation.

Latching the Data Byte

always @(posedge clk or posedge rst) begin

if (rst)

latched_data <= 8'h00;

else if (TX_start_rising) begin

latched_data <= TX_data_in;

$display("[UART_TX] BYTE = 0x%h", TX_data_in);


end

end

Why this matters:


You don't want TX_data_in to change in the middle of a transmission. So you store it in
latched_data and send only that.

Submodule Connections

TX_MUX inst_mux (...)

MUX chooses what bit to send:

• Start bit (0)

• Data bit (from PISO)

• Parity bit

• Stop bit (1)

TX_PISO inst_PISO (...)

PISO (Parallel-In Serial-Out):

• Loads the latched 8-bit data when load is high.

• Shifts right (LSB-first) when shift is high.

TX_PARITY inst_PARITY (...)

Calculates the even parity bit based on the 8 data bits.

TX_FSM inst_FSM (...)

Finite State Machine that:

• Controls MUX select

• Triggers loading and shifting

• Keeps track of transmission timing

• Raises TX_busy during transmission

Monitoring TX_busy

reg TX_busy_d;
always @(posedge clk) begin

TX_busy_d <= TX_busy;

if (TX_busy && !TX_busy_d)

$display("[UART_TX] TX_busy asserted (transmission starting)");

if (!TX_busy && TX_busy_d)

$display("[UART_TX] TX_busy de-asserted (transmission complete)");

end

Prints debug logs when transmission starts or ends.

Summary for UART_TX.v:

This module:

• Detects start signal (TX_start)

• Latches the data

• Controls the submodules that send a UART frame:

o [Start Bit] → [8 Data Bits] → [Parity Bit] → [Stop Bit]

• Sends data serially through TX_data_out

• Raises TX_busy during transmission

✅ MODULE 2: TX_PISO.v (Parallel-In Serial-Out Shift Register)

Purpose:

This module loads an 8-bit parallel input (piso_in) and shifts it out serially, one bit at a
time, from LSB (bit 0) to MSB (bit 7) — which is how UART sends data.

Inputs and Outputs:

input clk, rst, // Standard clock and reset

input load, // Load signal (high = capture input data)


input shift, // Shift signal (high = shift 1 bit right)

input [7:0] piso_in, // 8-bit data to be transmitted

output piso_out // Current bit to be sent (LSB of register)

Internal Storage:

reg [7:0] shift_reg;

A temporary 8-bit shift register:

• Initially empty.

• When load = 1, it loads piso_in.

• When shift = 1, it right-shifts by 1 (bit 1 → bit 0, etc.), and pads with 0 on the left.

Logic Block: Load or Shift

always @(posedge clk or posedge rst)

Trigger on rising edge of clock or reset.

if (rst)

shift_reg <= 8'b0;

Reset clears the shift register.

else if (load)

shift_reg <= piso_in;

If load is high, capture the full byte into shift_reg.

else if (shift)

shift_reg <= {1'b0, shift_reg[7:1]};

If shift is high, move all bits one position right.

• The LSB (shift_reg[0]) is now ready to be sent.

• 1'b0 is shifted into MSB to preserve framing.

Serial Output:

assign piso_out = shift_reg[0];


The least significant bit (LSB) is the current output bit that gets sent through UART.

Debug Logging:

if (load)

$display("[PISO] Loading data: 0x%h (binary: %b)", piso_in, piso_in);

else if (shift)

$display("[PISO] Shifting, current reg: %b, output bit: %b", shift_reg, shift_reg[0]);

✅ MODULE 3: TX_PARITY.v

Purpose:

Calculates the even parity bit for the data.

• Even parity means: total number of 1’s (including parity) should be even.

Inputs and Outputs:

input clk, rst;

input [7:0] parity_data_in; // 8-bit data to calculate parity on

input parity_load; // When high, calculate and latch parity

output reg parity_out; // The calculated even parity bit

Logic:

always @ (posedge clk or posedge rst)

Run on each clock cycle or reset.

if (rst)

parity_out <= 1'b0;

Clear parity bit on reset.

else if (parity_load)

parity_out <= ^(parity_data_in);


^ is the reduction XOR operator → XORs all bits of parity_data_in.
If result is:

• 1: There were an odd number of 1's → parity = 1 (to make it even)

• 0: Even number of 1's → parity = 0 (already even)

Debug Message:

$display("[PARITY] CALCULATED EVEN PARITY = %b for 0x%h", ^(parity_data_in),


parity_data_in);

✅ MODULE 4: TX_MUX.v

Purpose:

This is a 4-to-1 multiplexer that decides what bit goes to the UART TX line (tx_out):

• start bit (always 0)

• data bits (from shift register)

• parity bit

• stop bit (always 1)

Inputs and Outputs:

input [1:0] select; // 2-bit select: chooses start/data/parity/stop

input data_bit; // From PISO

input parity_bit; // From TX_PARITY

output reg tx_out; // Output to UART TX pin

Logic:

always @(*) begin

case (select)

2'b00: tx_out = 1'b0; // Start bit


2'b01: tx_out = data_bit; // Data bit

2'b10: tx_out = parity_bit; // Parity

2'b11: tx_out = 1'b1; // Stop bit

default: tx_out = 1'b1; // Default to stop

endcase

end

The FSM chooses what to send using select.

✅ MODULE 5: TX_FSM.v (Finite State Machine for UART Transmission)

Purpose:

Controls the entire sequence of UART transmission:

START → [8 DATA BITS] → PARITY → STOP

Each part lasts for 1 or more clock cycles. This FSM generates:

• load for loading data

• shift for PISO

• parity_load

• select signal for the MUX

• TX_busy to signal transmission is active

Inputs and Outputs:

input clk, rst, TX_start;

output reg [1:0] select; // Controls the MUX

output reg load; // Loads data into PISO

output reg shift; // Enables shifting in PISO

output reg parity_load; // Triggers parity calculation

output reg TX_busy; // High when transmission is active


Internal Registers

reg [3:0] count;

reg count_en;

reg [2:0] present_state, next_state;

• count: Counts how many data bits have been shifted.

• count_en: Enables the counter during the DATA phase.

• present_state, next_state: FSM state registers.

Counter Logic:

if (count_en)

count <= count + 1;

else

count <= 0;

count increments when sending data bits.

wire data_done = (count == 7);

Done when 8 data bits are sent (0–7 = 8 cycles).

FSM STATES:

parameter IDLE = 3'b000,

START = 3'b001,

DATA = 3'b010,

PARITY = 3'b011,

STOP = 3'b100;

State Register:

always @(posedge clk or posedge rst)

present_state <= (rst) ? IDLE : next_state;


Next State Logic:

case (present_state)

IDLE: if (TX_start) next_state = START;

START: next_state = DATA;

DATA: if (data_done) next_state = PARITY;

PARITY: next_state = STOP;

STOP: next_state = IDLE;

endcase

Moves through the UART transmission steps.

Output Control Signals:

always @(*) begin

load = 0;

shift = 0;

parity_load = 0;

count_en = 0;

TX_busy = 1; // Default: busy

select = 2'b11; // Default: stop bit (idle high)

case (present_state)

IDLE: begin

TX_busy = 0;

select = 2'b11; // Line idle (stop bit)

end

START: begin

load = 1;

parity_load = 1;

select = 2'b00; // Send start bit


end

DATA: begin

shift = 1;

count_en = 1;

select = 2'b01; // Send data bits

end

PARITY: select = 2'b10;

STOP: select = 2'b11;

endcase

Debug Display:

$display("[TX_FSM] START state: Sending start bit (0)");

$display("[TX_FSM] DATA state: Sending bit %0d", count);

$display("[TX_FSM] PARITY state: Sending parity bit");

$display("[TX_FSM] STOP state: Sending stop bit (1)");

Final Notes:

Full UART Frame Order:

[Start Bit (0)] → [Data Bits (LSB first)] → [Even Parity Bit] → [Stop Bit (1)]

Each is sent over multiple clock cycles, managed by the FSM, and output via tx_out.

You might also like