RAM Programmer - UART-Based RAM Programming Technical Documentation¶
Contents¶
- Overview
- Module Interface
- Programming Protocol
- FSM Details
- UART Receiver
- RAM Writes
- Reset Management
Overview¶
Purpose¶
The ram_programmer module, provides UART-based RAM programming. On detecting the magic sequence it resets the system, receives program data, and writes RAM.
File Location¶
Programming Flow¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β RAM PROGRAMMING FLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββ ββββββββββββββββ ββββββββββββββ βββββββββββββββ β
β β IDLE βββββΊβ SEQ_RECEIVE βββββΊβ LEN_RECEIVEβββββΊβ PROGRAM β β
β β β β β β β β β β
β βββββββββββ β "LEVELTEST" β β Word Count β β Data Words β β
β β² ββββββββββββββββ ββββββββββββββ ββββββββ¬βββββββ β
β β β β
β β ββββββββββββββββββββββββββββββββββββββββββββ β β
β ββββββββββββ FINISH βββ β
β β Release Reset, Return to IDLE β β
β ββββββββββββββββββββββββββββββββββββββββββββ β
β β
β UART RX βββββββΊ[FSM]βββββββΊ RAM Write βββββββΊ System Reset β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Module Interface¶
Parametreler¶
module ram_programmer
import level_param::*;
#(
parameter int CPU_CLK = 50_000_000, // Clock frequency
parameter int PROG_BAUD_RATE = 115200, // UART baud rate
parameter string PROGRAM_SEQUENCE = "LEVELTEST" // 9-char magic sequence
)
Port Definitions¶
(
input logic i_clk,
input logic i_rst_n,
// UART RX Interface
input logic i_uart_rx,
// RAM Write Interface
output logic o_ram_we, // RAM write enable
output logic [31:0] o_ram_addr, // RAM write address
output logic [31:0] o_ram_wdata, // RAM write data
// System Control
output logic o_system_reset, // Active-low system reset
output logic o_prog_mode_led // Programming mode indicator
);
Programming Protocol¶
Protocol Format¶
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PROGRAMMING PROTOCOL β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββ βββββββββββββββββ βββββββββββββββββββββββββββββββββββ β
β β Magic Seq β β Word Count β β Data Words β β
β β 9 bytes β β 4 bytes (LE) β β N Γ 4 bytes (LE) β β
β β "LEVELTEST" β β N β β word[0], word[1], ... word[N-1] β β
β βββββββββββββββ βββββββββββββββββ βββββββββββββββββββββββββββββββββββ β
β β
β Total bytes = 9 + 4 + (N Γ 4) β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Example: 256 words (1KB program) β
β β
β "LEVELTEST" + 0x00000100 (LE) + 256 Γ 32-bit words β
β = 9 + 4 + 1024 = 1037 bytes total β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Little-Endian Format¶
// Word Count: N = 0x00001234
// UART byte order: 0x34, 0x12, 0x00, 0x00
// Data Word: 0xDEADBEEF
// UART byte order: 0xEF, 0xBE, 0xAD, 0xDE
FSM Details¶
State Enum¶
typedef enum logic [2:0] {
IDLE, // Normal operation, monitoring for magic sequence
SEQ_RECEIVE, // Receiving magic sequence
LEN_RECEIVE, // Receiving word count (4 bytes)
PROGRAM, // Receiving and writing data words
FINISH // Complete, releasing reset
} prog_state_e;
prog_state_e state_q, state_d;
State Machine¶
always_comb begin
state_d = state_q;
case (state_q)
IDLE: begin
// First byte of magic sequence detected
if (rx_valid && rx_data == PROGRAM_SEQUENCE[0]) begin
state_d = SEQ_RECEIVE;
end
end
SEQ_RECEIVE: begin
if (rx_valid) begin
if (seq_idx == SEQ_LEN - 1) begin
// Full sequence matched
state_d = LEN_RECEIVE;
end else if (rx_data != PROGRAM_SEQUENCE[seq_idx]) begin
// Mismatch - abort
state_d = IDLE;
end
end
end
LEN_RECEIVE: begin
if (rx_valid && byte_idx == 3) begin
// 4 bytes received
state_d = PROGRAM;
end
end
PROGRAM: begin
if (word_cnt == 0) begin
// All words received
state_d = FINISH;
end
end
FINISH: begin
// Wait a few cycles, then return to IDLE
if (finish_cnt == FINISH_DELAY) begin
state_d = IDLE;
end
end
endcase
end
State Diagram¶
rx_valid &&
rx == SEQ[0]
βββββββββββββββββββββββΊβββββββββββββββ
β β SEQ_RECEIVE β
βββββ΄ββββ β β
β ββββββββββββββββββββ€ mismatch β
β IDLE β ββββββββ¬βββββββ
β β β seq_complete
βββββββββ βΌ
β² βββββββββββββββ
β β LEN_RECEIVE β
β β β
β ββββββββ¬βββββββ
β β 4 bytes received
β βΌ
β βββββββββββββββ
β β PROGRAM β
β β β
β ββββββββ¬βββββββ
β β word_cnt == 0
β βΌ
β finish_delay βββββββββββββββ
ββββββββββββββββββββββββ€ FINISH β
β β
βββββββββββββββ
UART Receiver¶
Baud Rate Generator¶
localparam int CLKS_PER_BIT = CPU_CLK / PROG_BAUD_RATE;
logic [15:0] baud_cnt;
logic baud_tick;
always_ff @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
baud_cnt <= '0;
baud_tick <= 1'b0;
end else if (rx_active) begin
if (baud_cnt == CLKS_PER_BIT - 1) begin
baud_cnt <= '0;
baud_tick <= 1'b1;
end else begin
baud_cnt <= baud_cnt + 1;
baud_tick <= 1'b0;
end
end else begin
baud_cnt <= CLKS_PER_BIT / 2; // Sample at bit center
end
end
UART Receive FSM¶
typedef enum logic [1:0] {
RX_IDLE,
RX_START,
RX_DATA,
RX_STOP
} rx_state_e;
rx_state_e rx_state;
logic [2:0] bit_idx;
logic [7:0] rx_shift;
always_ff @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
rx_state <= RX_IDLE;
rx_valid <= 1'b0;
end else begin
rx_valid <= 1'b0;
case (rx_state)
RX_IDLE: begin
if (!i_uart_rx) begin // Start bit detected
rx_state <= RX_START;
end
end
RX_START: begin
if (baud_tick) begin
if (!i_uart_rx) begin // Confirm start bit
rx_state <= RX_DATA;
bit_idx <= '0;
end else begin
rx_state <= RX_IDLE; // False start
end
end
end
RX_DATA: begin
if (baud_tick) begin
rx_shift <= {i_uart_rx, rx_shift[7:1]}; // LSB first
if (bit_idx == 7) begin
rx_state <= RX_STOP;
end else begin
bit_idx <= bit_idx + 1;
end
end
end
RX_STOP: begin
if (baud_tick) begin
if (i_uart_rx) begin // Valid stop bit
rx_data <= rx_shift;
rx_valid <= 1'b1;
end
rx_state <= RX_IDLE;
end
end
endcase
end
end
RAM Writes¶
Address Counter¶
// Reset vector aligned address
localparam logic [31:0] RAM_BASE = 32'h8000_0000;
logic [31:0] write_addr;
always_ff @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
write_addr <= RAM_BASE;
end else if (state_q == LEN_RECEIVE && state_d == PROGRAM) begin
// Reset address at start of programming
write_addr <= RAM_BASE;
end else if (word_write_en) begin
// Increment by 4 (word-aligned)
write_addr <= write_addr + 4;
end
end
Word Assembly¶
logic [31:0] word_buffer;
logic [1:0] byte_idx;
logic word_complete;
always_ff @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
byte_idx <= '0;
end else if (state_q == PROGRAM && rx_valid) begin
// Little-endian assembly
case (byte_idx)
2'b00: word_buffer[7:0] <= rx_data;
2'b01: word_buffer[15:8] <= rx_data;
2'b10: word_buffer[23:16] <= rx_data;
2'b11: word_buffer[31:24] <= rx_data;
endcase
byte_idx <= byte_idx + 1;
end
end
assign word_complete = (byte_idx == 2'b11) && rx_valid;
RAM Write Generation¶
always_ff @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
o_ram_we <= 1'b0;
o_ram_addr <= '0;
o_ram_wdata <= '0;
end else begin
o_ram_we <= 1'b0;
if (word_complete) begin
o_ram_we <= 1'b1;
o_ram_addr <= write_addr;
o_ram_wdata <= {rx_data, word_buffer[23:0]}; // Complete word
end
end
end
Reset Management¶
System Reset Control¶
// Hold CPU in reset during programming
always_ff @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
o_system_reset <= 1'b1; // Active (CPU running)
end else begin
case (state_q)
IDLE: begin
o_system_reset <= 1'b1; // Normal operation
end
SEQ_RECEIVE, LEN_RECEIVE, PROGRAM: begin
o_system_reset <= 1'b0; // Hold CPU in reset
end
FINISH: begin
// Release reset with delay
if (finish_cnt >= FINISH_DELAY) begin
o_system_reset <= 1'b1;
end
end
endcase
end
end
Programming Mode LED¶
Timing Diagram¶
Successful Programming¶
Magic Seq Len Data Words
βββββββββββββ βββββββ βββββββββββββββββββββ
i_uart_rx βββββ€LEVELTEST ββββββββ€ N ββββββ€ W0 W1 W2 ... Wn βββββββββ
βββββββββββββ βββββββ βββββββββββββββββββββ
state βββββ¬ββββ¬βββββββββββββββ¬ββββββββββ¬βββββββββββββββββββββ¬ββββ¬ββββ
IDLEβSEQβ SEQ_RECV βLEN_RECV β PROGRAM βFINβIDLE
o_system_reset βββββββββ ββββββββ
βββββββββββββββββββββββββββββββββββββββββββ
o_prog_mode_led βββββββββββββββββββββββββββββββββββββββββββ
ββββββββββ ββββββββ
o_ram_we βββ βββ βββ βββ
ββββββββββββββββββββββββββββββββ βββ βββ ββ...ββ ββββββββββ
Usage Example¶
Python Programming Script¶
#!/usr/bin/env python3
import serial
import struct
MAGIC_SEQUENCE = b"LEVELTEST"
def program_ram(port, binary_file, baud=115200):
"""Program Level SoC RAM via UART"""
with open(binary_file, 'rb') as f:
data = f.read()
# Pad to word alignment
while len(data) % 4 != 0:
data += b'\x00'
word_count = len(data) // 4
with serial.Serial(port, baud, timeout=5) as ser:
# Send magic sequence
ser.write(MAGIC_SEQUENCE)
# Send word count (little-endian)
ser.write(struct.pack('<I', word_count))
# Send data
ser.write(data)
print(f"Programmed {word_count} words ({len(data)} bytes)")
if __name__ == "__main__":
import sys
program_ram(sys.argv[1], sys.argv[2])
Summary¶
The ram_programmer module:
- Magic Sequence: "LEVELTEST" detection
- Protocol: Sequence β Word Count β Data
- FSM: 5-state programming controller
- UART: Built-in 8N1 receiver
- RAM Write: Word-aligned, little-endian
- Reset: CPU held during programming
This module enables loading a boot program on FPGA.