Skip to content

RAM Programmer - UART-Based RAM Programming Technical Documentation

Contents

  1. Overview
  2. Module Interface
  3. Programming Protocol
  4. FSM Details
  5. UART Receiver
  6. RAM Writes
  7. 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

rtl/wrapper/ram_programmer.sv

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

// LED indicator during programming
assign o_prog_mode_led = (state_q != IDLE);

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:

  1. Magic Sequence: "LEVELTEST" detection
  2. Protocol: Sequence β†’ Word Count β†’ Data
  3. FSM: 5-state programming controller
  4. UART: Built-in 8N1 receiver
  5. RAM Write: Word-aligned, little-endian
  6. Reset: CPU held during programming

This module enables loading a boot program on FPGA.