Magic‐1 Verilog Implementation - retrotruestory/M1DEV GitHub Wiki

Magic-1 CPU Verilog Implementation - Part 1: Core ALU and Control Path

Let's start by implementing the core ALU and control path for the Magic-1 CPU in Verilog. I notice your current ALU implementation needs completion and refinement.

ALU Implementation

First, let's fix and complete the ALU module:

module magic1_alu (
    input wire [15:0] left_in,
    input wire [15:0] right_in,
    input wire [3:0] operation,
    output reg [15:0] result,
    output reg carry,
    output reg zero,
    output reg sign,
    output reg overflow
);

// ALU Operation codes (matches Magic-1 microcode)
parameter ALU_ADD = 4'b0000,
          ALU_SUB = 4'b0001,
          ALU_AND = 4'b0010,
          ALU_OR  = 4'b0011,
          ALU_XOR = 4'b0100,
          ALU_SHL = 4'b0101,
          ALU_SHR = 4'b0110,
          ALU_ASR = 4'b0111,
          ALU_ROL = 4'b1000,
          ALU_ROR = 4'b1001;

reg [16:0] temp; // For carry calculations

always @(*) begin
    // Default assignments
    carry = 1'b0;
    overflow = 1'b0;
    
    case (operation)
        ALU_ADD: begin
            temp = {1'b0, left_in} + {1'b0, right_in};
            result = temp[15:0];
            carry = temp[16];
            overflow = (left_in[15] == right_in[15]) && 
                      (result[15] != left_in[15]);
        end
        
        ALU_SUB: begin
            temp = {1'b0, left_in} - {1'b0, right_in};
            result = temp[15:0];
            carry = temp[16];
            overflow = (left_in[15] != right_in[15]) && 
                      (result[15] != left_in[15]);
        end
        
        ALU_AND: result = left_in & right_in;
        ALU_OR:  result = left_in | right_in;
        ALU_XOR: result = left_in ^ right_in;
        
        ALU_SHL: begin
            result = {left_in[14:0], 1'b0};
            carry = left_in[15];
        end
        
        ALU_SHR: begin
            result = {1'b0, left_in[15:1]};
            carry = left_in[0];
        end
        
        ALU_ASR: begin
            result = {left_in[15], left_in[15:1]};
            carry = left_in[0];
        end
        
        ALU_ROL: begin
            result = {left_in[14:0], left_in[15]};
            carry = left_in[15];
        end
        
        ALU_ROR: begin
            result = {left_in[0], left_in[15:1]};
            carry = left_in[0];
        end
        
        default: result = 16'h0000;
    endcase
    
    // Update status flags
    zero = (result == 16'h0000);
    sign = result[15];
end

endmodule

Key features of this implementation:

  1. Precise Flag Handling:

    • Carry flag for arithmetic/shift operations
    • Overflow detection for signed arithmetic
    • Zero flag for comparison operations
    • Sign flag for signed number operations
  2. Complete ALU Operations:

    • Basic arithmetic (ADD, SUB)
    • Logical operations (AND, OR, XOR)
    • Shifts (SHL, SHR, ASR)
    • Rotates (ROL, ROR)
  3. Hardware-accurate Behavior:

    • 16-bit operation width
    • Proper carry propagation
    • Correct overflow detection
    • Matches Magic-1 specifications

Magic-1 CPU Verilog Implementation - Part 2: Bus Controller

Based on the Magic-1 schematics, let's implement the bus controller. This component manages data flow between registers, memory, and ALU.

module magic1_bus_controller #(
    parameter WIDTH = 16,
    parameter NUM_REGISTERS = 8
)(
    // Clock and control
    input wire clk,
    input wire reset_n,
    
    // Bus control signals from microcode
    input wire [3:0] l_bus_sel,     // Left bus source select
    input wire [2:0] r_bus_sel,     // Right bus source select
    input wire mem_read,            // Memory read enable
    input wire mem_write,           // Memory write enable
    
    // Register interface
    input wire [WIDTH-1:0] reg_data[NUM_REGISTERS-1:0],
    input wire [WIDTH-1:0] mdr_in,   // Memory Data Register input
    input wire [WIDTH-1:0] ir_in,    // Instruction Register input
    input wire [WIDTH-1:0] alu_in,   // ALU result input
    
    // Output buses
    output reg [WIDTH-1:0] l_bus,    // Left bus output
    output reg [WIDTH-1:0] r_bus,    // Right bus output
    
    // Memory interface
    output reg [WIDTH-1:0] mem_addr, // Memory address
    output reg [WIDTH-1:0] mem_data  // Memory data
);

// Bus source selection constants
localparam [3:0]
    L_ALU  = 4'b0000,    // ALU result
    L_MDR  = 4'b0001,    // Memory Data Register
    L_PC   = 4'b0010,    // Program Counter
    L_SP   = 4'b0011,    // Stack Pointer
    L_DP   = 4'b0100,    // Data Pointer
    L_A    = 4'b0101,    // A Register
    L_B    = 4'b0110,    // B Register
    L_C    = 4'b0111,    // C Register
    L_IR   = 4'b1000;    // Instruction Register

// Left bus multiplexer
always @(*) begin
    case (l_bus_sel)
        L_ALU:  l_bus = alu_in;
        L_MDR:  l_bus = mdr_in;
        L_IR:   l_bus = ir_in;
        default: begin
            if (l_bus_sel < NUM_REGISTERS)
                l_bus = reg_data[l_bus_sel];
            else
                l_bus = {WIDTH{1'b0}};
        end
    endcase
end

// Right bus multiplexer
always @(*) begin
    case (r_bus_sel)
        3'b000: r_bus = reg_data[0];  // PC
        3'b001: r_bus = reg_data[1];  // SP
        3'b010: r_bus = reg_data[2];  // DP
        3'b011: r_bus = reg_data[3];  // A
        3'b100: r_bus = reg_data[4];  // B
        3'b101: r_bus = reg_data[5];  // C
        default: r_bus = {WIDTH{1'b0}};
    endcase
end

// Memory interface logic
always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        mem_addr <= {WIDTH{1'b0}};
        mem_data <= {WIDTH{1'b0}};
    end else begin
        if (mem_read || mem_write) begin
            mem_addr <= l_bus;
            mem_data <= (mem_write) ? r_bus : {WIDTH{1'b0}};
        end
    end
end

endmodule

Key Features:

  1. Dual Bus Architecture

    • Left bus (L-bus) for general routing
    • Right bus (R-bus) for ALU and memory operations
    • Independent source selection
  2. Register Interface

    • Supports all Magic-1 registers
    • Direct routing to/from register file
    • Special handling for MDR and IR
  3. Memory Interface

    • Synchronous memory operations
    • Address from L-bus
    • Data from R-bus for writes
    • Support for read/write cycles

Magic-1 CPU Verilog Implementation - Part 3: Register File

Based on the Magic-1 schematics, let's implement the register file module.

Register File Architecture

The Magic-1 uses a multi-register architecture with:

  • General purpose registers (A, B, C)
  • Special purpose registers (PC, SP, DP)
  • Control registers (MSW)
  • Internal registers (MAR, MDR, IR)

Here's the implementation:

module magic1_register_file #(
    parameter WIDTH = 16
)(
    // Clock and control
    input wire clk,
    input wire reset_n,
    
    // Register control signals
    input wire [3:0] latch_sel,      // Register latch select
    input wire latch_mar,            // Memory Address Register latch
    input wire latch_mdr_lo,         // Memory Data Register low byte
    input wire latch_mdr_hi,         // Memory Data Register high byte
    input wire latch_ir,             // Instruction Register latch
    
    // Data inputs
    input wire [WIDTH-1:0] l_bus,    // Left bus input
    input wire [WIDTH-1:0] alu_result, // ALU result input
    input wire [WIDTH-1:0] mem_data,  // Memory data input
    
    // Register outputs
    output reg [WIDTH-1:0] reg_a,    // A register
    output reg [WIDTH-1:0] reg_b,    // B register
    output reg [WIDTH-1:0] reg_c,    // C register
    output reg [WIDTH-1:0] reg_pc,   // Program Counter
    output reg [WIDTH-1:0] reg_sp,   // Stack Pointer
    output reg [WIDTH-1:0] reg_dp,   // Data Pointer
    output reg [WIDTH-1:0] reg_mar,  // Memory Address Register
    output reg [WIDTH-1:0] reg_mdr,  // Memory Data Register
    output reg [WIDTH-1:0] reg_ir,   // Instruction Register
    output reg [15:0] reg_msw        // Machine Status Word
);

// Register latch selects
localparam [3:0]
    LATCH_NONE = 4'b0000,
    LATCH_A    = 4'b0001,
    LATCH_B    = 4'b0010,
    LATCH_C    = 4'b0011,
    LATCH_PC   = 4'b0100,
    LATCH_SP   = 4'b0101,
    LATCH_DP   = 4'b0110,
    LATCH_MSW  = 4'b0111;

// Synchronous register updates
always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        // Reset all registers
        reg_a   <= 16'h0000;
        reg_b   <= 16'h0000;
        reg_c   <= 16'h0000;
        reg_pc  <= 16'h0000;
        reg_sp  <= 16'hFFFF;  // Stack grows down
        reg_dp  <= 16'h0000;
        reg_mar <= 16'h0000;
        reg_mdr <= 16'h0000;
        reg_ir  <= 16'h0000;
        reg_msw <= 16'h0000;
    end else begin
        // Regular register updates
        case (latch_sel)
            LATCH_A:   reg_a   <= l_bus;
            LATCH_B:   reg_b   <= l_bus;
            LATCH_C:   reg_c   <= l_bus;
            LATCH_PC:  reg_pc  <= l_bus;
            LATCH_SP:  reg_sp  <= l_bus;
            LATCH_DP:  reg_dp  <= l_bus;
            LATCH_MSW: reg_msw <= l_bus;
        endcase

        // Special register handling
        if (latch_mar) begin
            reg_mar <= l_bus;
        end
        
        if (latch_mdr_lo) begin
            reg_mdr[7:0] <= l_bus[7:0];
        end
        
        if (latch_mdr_hi) begin
            reg_mdr[15:8] <= l_bus[15:8];
        end
        
        if (latch_ir) begin
            reg_ir <= mem_data;
        end
    end
end

endmodule

Key Features

  1. Register Organization:
  • General purpose (A, B, C)
  • Address registers (PC, SP, DP)
  • Memory interface (MAR, MDR)
  • Status and control (MSW, IR)
  1. Control Signals:
  • Individual latch enables
  • Byte-addressable MDR
  • Reset handling
  • Clock synchronization
  1. Data Routing:
  • L-bus input for most registers
  • Special handling for IR and MDR
  • Memory data path for instruction fetch

Magic-1 CPU Implementation Guide - Part 4: Control Unit

Based on the Magic-1 schematics, here's the control unit implementation with proper signal timing and microcode handling.

Control Unit Core Implementation

module magic1_control_unit #(
    parameter MICROCODE_WIDTH = 56,
    parameter MICROCODE_DEPTH = 512
)(
    // Clock and reset
    input  wire clk,
    input  wire reset_n,
    
    // Instruction input
    input  wire [15:0] instruction,
    input  wire [7:0]  condition_codes,
    
    // Microcode control outputs
    output reg [7:0]  next_address,
    output reg [3:0]  register_latch,
    output reg        mar_latch,
    output reg        mdr_lo_latch,
    output reg        mdr_hi_latch,
    output reg [3:0]  misc_control,
    output reg [3:0]  l_bus_enable,
    output reg [1:0]  r_bus_enable,
    output reg [1:0]  immediate_value,
    output reg [3:0]  alu_operation,
    output reg [1:0]  memory_control,
    output reg [1:0]  page_control
);

// Microcode ROM storage
reg [MICROCODE_WIDTH-1:0] microcode_rom[0:MICROCODE_DEPTH-1];

// Current microinstruction state
reg [MICROCODE_WIDTH-1:0] current_microinstruction;
reg [7:0] micro_address;

// Initialize microcode ROM
initial begin
    $readmemh("microcode.hex", microcode_rom);
end

// Microcode fetch and decode
always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        micro_address <= 8'h00;
        current_microinstruction <= {MICROCODE_WIDTH{1'b0}};
    end else begin
        // Fetch microinstruction
        current_microinstruction <= microcode_rom[micro_address];
        
        // Decode opcode for initial microcode address
        if (micro_address == 8'h00) begin
            micro_address <= instruction[15:8];
        end else begin
            // Next address from current microinstruction
            micro_address <= current_microinstruction[7:0];
        end
    end
end

// Control signal generation
always @(*) begin
    // Decompose current microinstruction into control signals
    next_address   = current_microinstruction[7:0];
    register_latch = current_microinstruction[11:8];
    mar_latch      = current_microinstruction[12];
    mdr_lo_latch   = current_microinstruction[13];
    mdr_hi_latch   = current_microinstruction[14];
    misc_control   = current_microinstruction[18:15];
    l_bus_enable   = current_microinstruction[22:19];
    r_bus_enable   = current_microinstruction[24:23];
    immediate_value = current_microinstruction[26:25];
    alu_operation  = current_microinstruction[30:27];
    memory_control = current_microinstruction[32:31];
    page_control   = current_microinstruction[34:33];
end

endmodule

Key Features

  1. Microcode ROM

    • 512 x 56-bit microcode storage
    • Initialized from hex file
    • Supports full Magic-1 instruction set
  2. Control Signal Generation

    • Clock synchronized operation
    • Reset handling
    • Proper signal timing
    • Instruction decoding
  3. Microinstruction Fields

    • Next address control
    • Register control
    • Bus routing
    • ALU operations
    • Memory operations

Magic-1 CPU Implementation Guide - Part 5: Memory Controller

Based on the Magic-1 schematics, let's implement the memory controller with proper bank switching and paging support.

Memory Controller Implementation

module magic1_memory_controller #(
    parameter ADDR_WIDTH = 16,
    parameter PAGE_SIZE = 1024,
    parameter NUM_PAGES = 32
)(
    // Clock and control
    input  wire clk,
    input  wire reset_n,
    
    // CPU interface
    input  wire [ADDR_WIDTH-1:0] address,
    input  wire [15:0] write_data,
    input  wire read_enable,
    input  wire write_enable,
    input  wire [1:0] page_control,
    
    // Memory status
    output reg ready,
    output reg page_fault,
    output reg [15:0] read_data,
    
    // Page table interface
    input  wire [15:0] ptb_reg,        // Page Table Base Register
    output reg [15:0] physical_addr
);

// Page control operations
localparam [1:0]
    PAGE_NONE = 2'b00,  // No paging
    PAGE_CODE = 2'b01,  // Code page access
    PAGE_DATA = 2'b10,  // Data page access
    PAGE_SYS  = 2'b11;  // System page access

// Page table entry structure
reg [15:0] page_table[0:NUM_PAGES-1];
reg [9:0]  page_number;
reg [9:0]  page_offset;
reg [15:0] pte;        // Page Table Entry

// Memory timing states
reg [1:0] mem_state;
localparam [1:0]
    IDLE    = 2'b00,
    LOOKUP  = 2'b01,
    ACCESS  = 2'b10,
    WAIT    = 2'b11;

// Page table lookup and address translation
always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        ready <= 1'b0;
        page_fault <= 1'b0;
        mem_state <= IDLE;
    end else begin
        case (mem_state)
            IDLE: begin
                if (read_enable || write_enable) begin
                    // Extract page number and offset
                    page_number <= address[15:10];
                    page_offset <= address[9:0];
                    mem_state <= (page_control != PAGE_NONE) ? LOOKUP : ACCESS;
                    ready <= 1'b0;
                end
            end
            
            LOOKUP: begin
                // Read page table entry
                pte <= page_table[page_number];
                
                // Check page permissions
                if (!pte[15]) begin  // Present bit
                    page_fault <= 1'b1;
                    mem_state <= IDLE;
                end else begin
                    physical_addr <= {pte[14:4], page_offset};
                    mem_state <= ACCESS;
                end
            end
            
            ACCESS: begin
                // Perform memory operation
                if (write_enable) begin
                    mem_write(physical_addr, write_data);
                end else if (read_enable) begin
                    read_data <= mem_read(physical_addr);
                end
                ready <= 1'b1;
                mem_state <= WAIT;
            end
            
            WAIT: begin
                ready <= 1'b0;
                mem_state <= IDLE;
            end
        endcase
    end
end

// Memory access tasks
task mem_write;
    input [15:0] addr;
    input [15:0] data;
    begin
        // Implementation specific memory write
    end
endtask

function [15:0] mem_read;
    input [15:0] addr;
    begin
        // Implementation specific memory read
    end
endfunction

endmodule

Key Features

  1. Memory Organization

    • 64KB address space
    • 1KB page size
    • 32 pages total
    • Separate code/data spaces
  2. Paging Support

    • Hardware page table
    • Page protection
    • Page fault detection
    • Physical address translation
  3. Memory Access Control

    • Read/write operations
    • Memory timing
    • Access permissions
    • Bank switching

Magic-1 CPU Implementation Guide - Part 6: Front Panel Interface

Based on the Magic-1 schematic's front panel design, here's the Verilog implementation for the front panel interface.

Interface Module

module magic1_front_panel (
    // Clock and control
    input wire clk,
    input wire reset_n,
    
    // Front panel switches and buttons
    input wire [15:0] address_switches,   // Address entry switches
    input wire [15:0] data_switches,      // Data entry switches
    input wire load_address_btn,          // Load address button
    input wire load_data_btn,             // Load data button
    input wire examine_btn,               // Examine memory button
    input wire examine_next_btn,          // Examine next button
    input wire deposit_btn,               // Deposit data button
    input wire deposit_next_btn,          // Deposit next button
    input wire single_step_btn,           // Single step button
    input wire run_btn,                   // Run button
    input wire reset_btn,                 // Reset button
    
    // LED displays
    output reg [15:0] address_display,    // Current address LEDs
    output reg [15:0] data_display,       // Current data LEDs
    output reg [7:0] status_leds,         // Status indicator LEDs
    
    // CPU interface
    output reg [15:0] fp_address,         // Address to CPU
    output reg [15:0] fp_data,            // Data to CPU
    output reg fp_write_enable,           // Write enable to memory
    output reg fp_read_enable,            // Read enable from memory
    output reg fp_run_enable,             // CPU run control
    output reg fp_step_enable,            // Single step control
    
    // Status inputs from CPU
    input wire [15:0] cpu_address,        // Current CPU address
    input wire [15:0] cpu_data,          // Current CPU data
    input wire [7:0] cpu_status          // CPU status flags
);

// Front panel states
localparam [3:0]
    FP_IDLE = 4'h0,
    FP_LOAD_ADDR = 4'h1,
    FP_EXAMINE = 4'h2,
    FP_EXAMINE_NEXT = 4'h3,
    FP_DEPOSIT = 4'h4,
    FP_DEPOSIT_NEXT = 4'h5,
    FP_RUN = 4'h6,
    FP_STEP = 4'h7;

reg [3:0] fp_state;
reg [15:0] current_address;

// Front panel control state machine
always @(posedge clk or negedge reset_n) begin
    if (!reset_n || reset_btn) begin
        fp_state <= FP_IDLE;
        current_address <= 16'h0000;
        fp_write_enable <= 1'b0;
        fp_read_enable <= 1'b0;
        fp_run_enable <= 1'b0;
        fp_step_enable <= 1'b0;
    end else begin
        case (fp_state)
            FP_IDLE: begin
                if (load_address_btn) begin
                    current_address <= address_switches;
                    fp_state <= FP_LOAD_ADDR;
                end else if (examine_btn) begin
                    fp_state <= FP_EXAMINE;
                    fp_read_enable <= 1'b1;
                end else if (run_btn) begin
                    fp_state <= FP_RUN;
                    fp_run_enable <= 1'b1;
                end
            end
            
            // Additional states and transitions...
        endcase
    end
end

// Display update logic
always @(posedge clk) begin
    if (fp_run_enable) begin
        address_display <= cpu_address;
        data_display <= cpu_data;
    end else begin
        address_display <= current_address;
        data_display <= fp_data;
    end
    status_leds <= cpu_status;
end

endmodule

Key Features

  1. Switch Interface

    • 16-bit address entry
    • 16-bit data entry
    • Control button debouncing
    • Mode selection
  2. Display Output

    • Address display LEDs
    • Data display LEDs
    • Status indicators
    • Real-time updates
  3. Control Functions

    • Memory examine/deposit
    • Single step execution
    • Run/stop control
    • Address/data loading

Magic-1 CPU Verilog Implementation - Part 7: System Bus Interface

Based on the Magic-1 hardware specifications, here's the system bus interface implementation.

Core Bus Interface Module

module magic1_system_bus #(
    parameter ADDR_WIDTH = 16,
    parameter DATA_WIDTH = 16,
    parameter NUM_DEVICES = 8
)(
    // Clock and reset
    input  wire clk,
    input  wire reset_n,
    
    // CPU side interface
    input  wire [ADDR_WIDTH-1:0] cpu_addr,
    input  wire [DATA_WIDTH-1:0] cpu_data_out,
    output reg  [DATA_WIDTH-1:0] cpu_data_in,
    input  wire cpu_read,
    input  wire cpu_write,
    output reg  cpu_ready,
    
    // Device interfaces
    output reg  [NUM_DEVICES-1:0] dev_select,
    output reg  [ADDR_WIDTH-1:0]  dev_addr,
    output reg  [DATA_WIDTH-1:0]  dev_data_out,
    input  wire [DATA_WIDTH-1:0]  dev_data_in [NUM_DEVICES-1:0],
    output reg  dev_read,
    output reg  dev_write,
    input  wire [NUM_DEVICES-1:0] dev_ready
);

// Device address map
localparam [ADDR_WIDTH-1:0]
    DEV_RAM_BASE  = 16'h0000,  // Main RAM
    DEV_ROM_BASE  = 16'hE000,  // Boot ROM
    DEV_UART_BASE = 16'hF000,  // UART
    DEV_IDE_BASE  = 16'hF100,  // IDE interface
    DEV_GPIO_BASE = 16'hF200;  // GPIO

// Bus state machine
reg [1:0] bus_state;
localparam [1:0]
    BUS_IDLE = 2'b00,
    BUS_SETUP = 2'b01,
    BUS_ACCESS = 2'b10,
    BUS_WAIT = 2'b11;

// Device selection logic
always @(*) begin
    dev_select = 8'h00;
    case (cpu_addr[15:12])
        4'h0, 4'h1, 4'h2, 4'h3: dev_select[0] = 1'b1;  // RAM
        4'hE: dev_select[1] = 1'b1;                     // ROM
        4'hF: begin
            case (cpu_addr[11:8])
                4'h0: dev_select[2] = 1'b1;             // UART
                4'h1: dev_select[3] = 1'b1;             // IDE
                4'h2: dev_select[4] = 1'b1;             // GPIO
            endcase
        end
    endcase
end

// Bus state machine
always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        bus_state <= BUS_IDLE;
        cpu_ready <= 1'b0;
        dev_read <= 1'b0;
        dev_write <= 1'b0;
    end else begin
        case (bus_state)
            BUS_IDLE: begin
                if (cpu_read || cpu_write) begin
                    dev_addr <= cpu_addr;
                    dev_data_out <= cpu_data_out;
                    bus_state <= BUS_SETUP;
                end
            end
            
            BUS_SETUP: begin
                dev_read <= cpu_read;
                dev_write <= cpu_write;
                bus_state <= BUS_ACCESS;
            end
            
            BUS_ACCESS: begin
                if (|(dev_select & dev_ready)) begin
                    // Select data from active device
                    for (int i = 0; i < NUM_DEVICES; i++) begin
                        if (dev_select[i])
                            cpu_data_in <= dev_data_in[i];
                    end
                    cpu_ready <= 1'b1;
                    bus_state <= BUS_WAIT;
                end
            end
            
            BUS_WAIT: begin
                dev_read <= 1'b0;
                dev_write <= 1'b0;
                cpu_ready <= 1'b0;
                bus_state <= BUS_IDLE;
            end
        endcase
    end
end

endmodule

Key Features

  1. Address Decoding

    • Device-specific address ranges
    • Multiple device support
    • Flexible device selection
  2. Bus Protocol

    • Four-phase handshake
    • Device ready signaling
    • Read/write control
    • Data routing
  3. Device Interface

    • Standardized device connections
    • Independent device timing
    • Parallel device support

Magic-1 CPU Verilog Implementation - Part 8: Interrupt Controller

Let's implement the interrupt controller based on the Magic-1 specifications.

Core Interrupt Architecture

The Magic-1 supports multiple interrupt sources with priority levels and masking capabilities.

Interrupt Controller Implementation

module magic1_interrupt_controller #(
    parameter NUM_INTERRUPTS = 8
)(
    // Clock and control
    input wire clk,
    input wire reset_n,
    
    // Interrupt inputs
    input wire [NUM_INTERRUPTS-1:0] interrupt_requests,
    input wire [15:0] msw_register,        // Machine Status Word
    
    // Interrupt outputs
    output reg interrupt_active,           // Interrupt pending
    output reg [7:0] interrupt_vector,     // Vector for current interrupt
    output reg interrupt_acknowledge,      // Ack to device
    
    // CPU interface
    input wire cpu_iack,                  // Interrupt acknowledge from CPU
    input wire cpu_cli,                   // Clear interrupt flag
    input wire cpu_sti                    // Set interrupt flag
);

// Interrupt priority encoder
reg [NUM_INTERRUPTS-1:0] active_interrupts;
reg [NUM_INTERRUPTS-1:0] interrupt_mask;
reg interrupt_enabled;

// Priority encoding logic
always @(*) begin
    interrupt_vector = 8'h00;
    interrupt_active = 1'b0;
    
    if (interrupt_enabled && (active_interrupts & ~interrupt_mask)) begin
        casez (active_interrupts & ~interrupt_mask)
            8'b1???????: begin 
                interrupt_vector = 8'h00;
                interrupt_active = 1'b1;
            end
            8'b01??????: begin
                interrupt_vector = 8'h08;
                interrupt_active = 1'b1;
            end
            8'b001?????: begin
                interrupt_vector = 8'h10;
                interrupt_active = 1'b1;
            end
            // ...additional priorities...
        endcase
    end
end

// Interrupt state management
always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        active_interrupts <= {NUM_INTERRUPTS{1'b0}};
        interrupt_mask <= {NUM_INTERRUPTS{1'b1}};
        interrupt_enabled <= 1'b0;
        interrupt_acknowledge <= 1'b0;
    end else begin
        // Update active interrupts
        active_interrupts <= interrupt_requests;
        
        // Handle CPU control
        if (cpu_cli) begin
            interrupt_enabled <= 1'b0;
        end else if (cpu_sti) begin
            interrupt_enabled <= 1'b1;
        end
        
        // Handle interrupt acknowledgment
        if (cpu_iack && interrupt_active) begin
            interrupt_acknowledge <= 1'b1;
            interrupt_mask[interrupt_vector[7:3]] <= 1'b1;
        end else begin
            interrupt_acknowledge <= 1'b0;
        end
    end
end

endmodule

Key Features

  1. Interrupt Management

    • 8 priority levels
    • Individual interrupt masking
    • Global interrupt enable/disable
    • Vector-based dispatching
  2. Priority System

    • Fixed priority encoding
    • Nested interrupt support
    • Automatic priority masking
    • Vector generation
  3. CPU Interface

    • Clean handshaking protocol
    • Status word integration
    • CLI/STI support
    • Interrupt acknowledgment

Magic-1 CPU Implementation Guide - Part 9: Debug Interface

The Magic-1 debug interface provides real-time monitoring and control capabilities essential for development and troubleshooting.

Core Debug Module

The debug interface implements:

  • Breakpoint management
  • Register inspection
  • Memory examination
  • Single-step control

Implementation

module magic1_debug_interface #(
    parameter NUM_BREAKPOINTS = 4,
    parameter ADDR_WIDTH = 16
)(
    // Clock and system control
    input wire clk,
    input wire reset_n,
    
    // CPU interface
    input wire [ADDR_WIDTH-1:0] cpu_pc,
    input wire [15:0] cpu_data,
    input wire cpu_executing,
    output reg cpu_break,
    output reg cpu_step,
    
    // Debug control interface
    input wire dbg_enable,
    input wire dbg_step_req,
    input wire dbg_break_set,
    input wire dbg_break_clear,
    input wire [ADDR_WIDTH-1:0] dbg_break_addr,
    
    // Debug status output
    output reg dbg_active,
    output reg [3:0] dbg_status,
    output reg [7:0] dbg_data,
    
    // UART interface for debug console
    input wire uart_rx,
    output wire uart_tx
);

// Breakpoint storage
reg [ADDR_WIDTH-1:0] breakpoints[NUM_BREAKPOINTS-1:0];
reg [NUM_BREAKPOINTS-1:0] breakpoint_valid;

// Debug state tracking
reg [2:0] debug_state;
reg step_pending;
reg [7:0] command_buffer;

// Debug command processor
always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        debug_state <= 3'b000;
        cpu_break <= 1'b0;
        cpu_step <= 1'b0;
        dbg_active <= 1'b0;
        breakpoint_valid <= 4'h0;
    end else begin
        // Process breakpoints
        if (cpu_executing) begin
            for (int i = 0; i < NUM_BREAKPOINTS; i++) begin
                if (breakpoint_valid[i] && cpu_pc == breakpoints[i]) begin
                    cpu_break <= 1'b1;
                    dbg_active <= 1'b1;
                    debug_state <= 3'b001;
                end
            end
        end
        
        // Handle single-step
        if (dbg_step_req && !step_pending) begin
            cpu_step <= 1'b1;
            step_pending <= 1'b1;
        end else if (cpu_executing && step_pending) begin
            cpu_step <= 1'b0;
            step_pending <= 1'b0;
            cpu_break <= 1'b1;
        end
    end
end

// Implement debug command processor here
// ...

endmodule

Debug Command Set

Key debug commands supported:

  1. Memory Operations

    • read <addr> - Read memory location
    • write <addr> <data> - Write memory location
    • dump <start> <len> - Memory dump
  2. Breakpoint Control

    • bp set <addr> - Set breakpoint
    • bp clear <addr> - Clear breakpoint
    • bp list - List breakpoints
  3. Execution Control

    • step - Single step
    • continue - Resume execution
    • halt - Stop execution

Magic-1 CPU Implementation Guide - Part 10: Clock Generation and Distribution

The Magic-1 requires precise clock generation and distribution for synchronous operation of all components.

Clock Generator Module

Key requirements:

  • Main system clock generation
  • Phase-related clocks for bus timing
  • Clock gating for power management
  • Reset synchronization

Implementation

module magic1_clock_gen #(
    parameter CLOCK_FREQ = 50_000_000,  // 50MHz input clock
    parameter CPU_FREQ = 10_000_000     // 10MHz CPU clock
)(
    // Primary clock input
    input wire ext_clk,        // External clock input
    input wire ext_reset_n,    // External reset (active low)
    
    // Generated clocks
    output reg sys_clk,        // System clock
    output reg cpu_clk,        // CPU clock
    output reg bus_clk,        // Bus interface clock
    output reg mem_clk,        // Memory subsystem clock
    
    // Clock control
    input wire clock_enable,   // Global clock enable
    input wire cpu_stall,      // CPU clock stall request
    
    // Reset outputs
    output reg sys_reset_n     // Synchronized system reset
);

// Clock divider calculation
localparam DIV_CPU = CLOCK_FREQ / CPU_FREQ;
localparam DIV_WIDTH = $clog2(DIV_CPU);

// Clock division counter
reg [DIV_WIDTH-1:0] div_counter;
reg [2:0] phase_counter;

// Reset synchronizer
reg [2:0] reset_sync;

// Clock generation
always @(posedge ext_clk) begin
    if (!ext_reset_n) begin
        div_counter <= 0;
        phase_counter <= 0;
        sys_clk <= 0;
        cpu_clk <= 0;
        bus_clk <= 0;
        mem_clk <= 0;
    end else if (clock_enable) begin
        // Main clock divider
        if (div_counter == DIV_CPU - 1) begin
            div_counter <= 0;
            phase_counter <= phase_counter + 1;
        end else begin
            div_counter <= div_counter + 1;
        end
        
        // Clock phase generation
        case (phase_counter)
            3'b000: begin 
                cpu_clk <= 1;
                bus_clk <= 0;
                mem_clk <= 0;
            end
            3'b010: begin
                cpu_clk <= cpu_stall ? 0 : 1;
                bus_clk <= 1;
                mem_clk <= 0;
            end
            3'b100: begin
                cpu_clk <= 0;
                bus_clk <= 0;
                mem_clk <= 1;
            end
            default: begin
                cpu_clk <= 0;
                bus_clk <= 0;
                mem_clk <= 0;
            end
        endcase
    end
end

// Reset synchronization
always @(posedge ext_clk) begin
    reset_sync <= {reset_sync[1:0], ext_reset_n};
    sys_reset_n <= reset_sync[2];
end

endmodule

Key Features

  1. Clock Generation

    • Configurable input/output frequencies
    • Multi-phase clock generation
    • Clock division with minimal jitter
  2. Reset Management

    • Synchronized reset distribution
    • Multi-stage reset synchronization
    • Clean reset deassertion
  3. Clock Control

    • Global clock enable
    • CPU stall support
    • Independent phase control

Magic-1 CPU Implementation Guide - Part 11: I/O Controller

Overview

The I/O controller manages peripheral interfaces and provides a standardized way to communicate with external devices. Based on the Magic-1 specifications, we'll implement a flexible I/O system.

Implementation

module magic1_io_controller #(
    parameter NUM_PORTS = 16,
    parameter PORT_WIDTH = 8
)(
    // Clock and control
    input  wire clk,
    input  wire reset_n,
    
    // CPU interface
    input  wire [7:0]  port_addr,     // I/O port address
    input  wire [15:0] write_data,    // Data to write
    output reg  [15:0] read_data,     // Data read from port
    input  wire io_read,              // I/O read strobe
    input  wire io_write,             // I/O write strobe
    output reg  io_ready,             // I/O operation complete
    
    // Hardware port interfaces
    input  wire [PORT_WIDTH-1:0] port_in[NUM_PORTS-1:0],   // Input ports
    output reg  [PORT_WIDTH-1:0] port_out[NUM_PORTS-1:0],  // Output ports
    output reg  [NUM_PORTS-1:0]  port_strobe              // Port strobes
);

// I/O port address map
localparam [7:0]
    UART_DATA    = 8'h00,    // UART data port
    UART_STATUS  = 8'h01,    // UART status port
    TIMER_COUNT  = 8'h10,    // Timer counter
    TIMER_CTRL   = 8'h11,    // Timer control
    GPIO_DATA    = 8'h20,    // GPIO data
    GPIO_DIR     = 8'h21;    // GPIO direction

// I/O state machine
reg [1:0] io_state;
localparam [1:0]
    IO_IDLE    = 2'b00,
    IO_ACTIVE  = 2'b01,
    IO_WAIT    = 2'b10,
    IO_DONE    = 2'b11;

// I/O operation handling
always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        io_state <= IO_IDLE;
        io_ready <= 1'b0;
        port_strobe <= {NUM_PORTS{1'b0}};
    end else begin
        case (io_state)
            IO_IDLE: begin
                if (io_read || io_write) begin
                    io_state <= IO_ACTIVE;
                    io_ready <= 1'b0;
                    // Decode port address and set strobe
                    port_strobe <= (1 << port_addr[3:0]);
                end
            end
            
            IO_ACTIVE: begin
                if (io_write) begin
                    case (port_addr)
                        UART_DATA: port_out[0] <= write_data[7:0];
                        UART_STATUS: port_out[1] <= write_data[7:0];
                        TIMER_COUNT: port_out[2] <= write_data[7:0];
                        GPIO_DATA: port_out[4] <= write_data[7:0];
                        default: /* Invalid port */;
                    endcase
                end else if (io_read) begin
                    case (port_addr)
                        UART_DATA: read_data <= {8'h00, port_in[0]};
                        UART_STATUS: read_data <= {8'h00, port_in[1]};
                        TIMER_COUNT: read_data <= {8'h00, port_in[2]};
                        GPIO_DATA: read_data <= {8'h00, port_in[4]};
                        default: read_data <= 16'h0000;
                    endcase
                end
                io_state <= IO_WAIT;
            end
            
            IO_WAIT: begin
                io_ready <= 1'b1;
                io_state <= IO_DONE;
            end
            
            IO_DONE: begin
                io_ready <= 1'b0;
                port_strobe <= {NUM_PORTS{1'b0}};
                io_state <= IO_IDLE;
            end
        endcase
    end
end

endmodule

Key Features

1. Port Management

  • 16 configurable I/O ports
  • 8-bit port width
  • Individual port strobes
  • Bidirectional data paths

2. Port Addressing

  • Dedicated address space for I/O
  • Standard port assignments
  • Reserved addresses for expansion

3. Timing Control

  • Synchronized I/O operations
  • Ready signaling
  • Port strobe generation

Magic-1 CPU Implementation Guide - Part 12: Timer Module

Overview

The Magic-1 timer module provides precise timing for system events and programmable intervals. Let's implement the core timer functionality.

Timer Module Implementation

module magic1_timer #(
    parameter CLOCK_FREQ = 50_000_000,  // 50MHz system clock
    parameter NUM_TIMERS = 4            // Number of timer channels
)(
    // Clock and control
    input  wire clk,
    input  wire reset_n,
    
    // Timer configuration interface
    input  wire [1:0]  timer_sel,      // Timer channel select
    input  wire [15:0] timer_load,     // Timer load value
    input  wire        timer_write,     // Write strobe
    input  wire [7:0]  timer_control,   // Timer control register
    output reg  [15:0] timer_value,    // Current timer value
    output reg  [3:0]  timer_irq       // Timer interrupt requests
);

// Timer control register bits
localparam 
    TIMER_ENABLE = 0,
    TIMER_MODE   = 1,    // 0=one-shot, 1=continuous
    TIMER_IRQ_EN = 2;

// Timer channel storage
reg [15:0] timer_counters[NUM_TIMERS-1:0];
reg [15:0] timer_reload[NUM_TIMERS-1:0];
reg [7:0]  timer_cfg[NUM_TIMERS-1:0];
reg [NUM_TIMERS-1:0] timer_running;

// Timer update logic
always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        for (int i = 0; i < NUM_TIMERS; i++) begin
            timer_counters[i] <= 16'h0000;
            timer_reload[i] <= 16'h0000;
            timer_cfg[i] <= 8'h00;
            timer_running[i] <= 1'b0;
        end
        timer_irq <= 4'h0;
    end else begin
        // Handle configuration writes
        if (timer_write) begin
            timer_reload[timer_sel] <= timer_load;
            timer_cfg[timer_sel] <= timer_control;
            if (timer_control[TIMER_ENABLE]) begin
                timer_counters[timer_sel] <= timer_load;
                timer_running[timer_sel] <= 1'b1;
            end
        end
        
        // Update active timers
        for (int i = 0; i < NUM_TIMERS; i++) begin
            if (timer_running[i]) begin
                if (timer_counters[i] == 16'h0000) begin
                    // Timer expired
                    if (timer_cfg[i][TIMER_IRQ_EN])
                        timer_irq[i] <= 1'b1;
                        
                    if (timer_cfg[i][TIMER_MODE]) begin
                        // Continuous mode - reload
                        timer_counters[i] <= timer_reload[i];
                    end else begin
                        // One-shot mode - stop
                        timer_running[i] <= 1'b0;
                    end
                end else begin
                    timer_counters[i] <= timer_counters[i] - 1;
                end
            end
        end
        
        // Output selected timer value
        timer_value <= timer_counters[timer_sel];
    end
end

endmodule

Key Features

1. Timer Configuration

  • Multiple independent timer channels
  • Programmable period/reload values
  • One-shot and continuous modes
  • Per-channel interrupt generation

2. Timer Control

  • Enable/disable control
  • Mode selection
  • Interrupt masking
  • Current value reading

3. Clock Management

  • Synchronized counting
  • Automatic reload handling
  • Clean reset behavior
  • Interrupt request generation

Magic-1 CPU Implementation Guide - Part 13: UART Controller

The UART controller provides serial communication capabilities for the Magic-1 system. Let's implement a 16550-compatible UART interface.

Core UART Implementation

module magic1_uart #(
    parameter CLOCK_FREQ = 50_000_000,
    parameter DEFAULT_BAUD = 9600
)(
    // Clock and control
    input  wire clk,
    input  wire reset_n,
    
    // CPU interface
    input  wire [2:0]  reg_addr,     // Register address
    input  wire [7:0]  write_data,   // Data to write
    output reg  [7:0]  read_data,    // Data read from registers
    input  wire        reg_write,    // Register write strobe
    input  wire        reg_read,     // Register read strobe
    output reg         uart_irq,     // UART interrupt request
    
    // Serial interface
    input  wire        rx,           // Serial receive
    output reg         tx,           // Serial transmit
    input  wire        cts_n,        // Clear to send
    output reg         rts_n         // Request to send
);

// UART registers
localparam [2:0]
    REG_RBR = 3'b000,    // Receive Buffer Register
    REG_THR = 3'b000,    // Transmit Holding Register
    REG_IER = 3'b001,    // Interrupt Enable Register
    REG_IIR = 3'b010,    // Interrupt Identification Register
    REG_LCR = 3'b011,    // Line Control Register
    REG_MCR = 3'b100,    // Modem Control Register
    REG_LSR = 3'b101,    // Line Status Register
    REG_MSR = 3'b110,    // Modem Status Register
    REG_SCR = 3'b111;    // Scratch Register

// Internal storage
reg [7:0] rx_buffer;      // Receive buffer
reg [7:0] tx_buffer;      // Transmit buffer
reg [7:0] ier;           // Interrupt enable
reg [7:0] lcr;           // Line control
reg [7:0] mcr;           // Modem control
reg [7:0] lsr;           // Line status
reg [7:0] msr;           // Modem status
reg [7:0] scr;           // Scratch

// Baud rate generation
reg [15:0] baud_divider;
reg [15:0] baud_counter;
reg baud_tick;

// Transmit/Receive state machines
reg [3:0] tx_state;
reg [3:0] rx_state;
reg [7:0] tx_shift;
reg [7:0] rx_shift;
reg [3:0] bit_count;

// Generate baud rate clock
always @(posedge clk) begin
    if (baud_counter == 0) begin
        baud_counter <= baud_divider;
        baud_tick <= 1'b1;
    end else begin
        baud_counter <= baud_counter - 1;
        baud_tick <= 1'b0;
    end
end

// UART register access
always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        // Reset all registers
        ier <= 8'h00;
        lcr <= 8'h00;
        mcr <= 8'h00;
        tx <= 1'b1;
        rts_n <= 1'b1;
    end else begin
        if (reg_write) begin
            case (reg_addr)
                REG_THR: begin
                    if (!lsr[5]) begin  // Check if THR empty
                        tx_buffer <= write_data;
                        lsr[5] <= 1'b0;  // Clear THR empty
                    end
                end
                REG_IER: ier <= write_data;
                REG_LCR: lcr <= write_data;
                REG_MCR: mcr <= write_data;
                REG_SCR: scr <= write_data;
            endcase
        end
        
        if (reg_read) begin
            case (reg_addr)
                REG_RBR: begin
                    read_data <= rx_buffer;
                    lsr[0] <= 1'b0;  // Clear data ready
                end
                REG_IER: read_data <= ier;
                REG_IIR: read_data <= {5'b0, ~uart_irq, 2'b0};
                REG_LCR: read_data <= lcr;
                REG_MCR: read_data <= mcr;
                REG_LSR: read_data <= lsr;
                REG_MSR: read_data <= msr;
                REG_SCR: read_data <= scr;
            endcase
        end
    end
end

endmodule

Key Features

1. Register Set

  • Standard 16550 UART registers
  • Programmable baud rate
  • FIFO support
  • Flow control signals

2. Serial Communications

  • Asynchronous serial protocol
  • Multiple baud rates
  • Configurable data formats
  • Hardware flow control

3. Interrupt Support

  • Multiple interrupt sources
  • Programmable enables
  • Status reporting
  • Error detection

Magic-1 CPU Implementation Guide - Part 14: DMA Controller

The DMA controller enables direct memory access transfers between devices and memory without CPU intervention. Based on the Magic-1 specifications, here's the implementation.

DMA Controller Module

module magic1_dma_controller #(
    parameter NUM_CHANNELS = 4,
    parameter ADDR_WIDTH = 16
)(
    // System interface
    input  wire clk,
    input  wire reset_n,
    
    // CPU interface
    input  wire [3:0] channel_select,
    input  wire [7:0] dma_command,
    input  wire [ADDR_WIDTH-1:0] dma_addr,
    input  wire [15:0] dma_count,
    output reg  dma_busy,
    output reg  dma_done,
    output reg  dma_error,
    
    // Memory interface
    output reg  [ADDR_WIDTH-1:0] mem_addr,
    output reg  [15:0] mem_data_out,
    input  wire [15:0] mem_data_in,
    output reg  mem_read,
    output reg  mem_write,
    input  wire mem_ready,
    
    // Device interface
    output reg  [NUM_CHANNELS-1:0] dev_select,
    output reg  dev_read,
    output reg  dev_write,
    output reg  [15:0] dev_data_out,
    input  wire [15:0] dev_data_in,
    input  wire dev_ready,
    
    // Interrupt
    output reg dma_irq
);

// DMA channel state
reg [ADDR_WIDTH-1:0] channel_addr [NUM_CHANNELS-1:0];
reg [15:0] channel_count [NUM_CHANNELS-1:0];
reg [7:0]  channel_config [NUM_CHANNELS-1:0];
reg [NUM_CHANNELS-1:0] channel_active;

// DMA state machine
reg [2:0] dma_state;
localparam [2:0]
    DMA_IDLE    = 3'b000,
    DMA_READ    = 3'b001,
    DMA_WRITE   = 3'b010,
    DMA_WAIT    = 3'b011,
    DMA_NEXT    = 3'b100,
    DMA_DONE    = 3'b101;

// DMA transfer logic
always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        dma_state <= DMA_IDLE;
        channel_active <= 0;
        dma_busy <= 0;
        dma_done <= 0;
        dma_error <= 0;
        mem_read <= 0;
        mem_write <= 0;
        dev_read <= 0;
        dev_write <= 0;
    end else begin
        case (dma_state)
            DMA_IDLE: begin
                if (|channel_active) begin
                    dma_state <= DMA_READ;
                    dma_busy <= 1;
                end
            end
            
            DMA_READ: begin
                if (channel_config[channel_select][0]) begin
                    // Memory to device
                    mem_addr <= channel_addr[channel_select];
                    mem_read <= 1;
                end else begin
                    // Device to memory
                    dev_select[channel_select] <= 1;
                    dev_read <= 1;
                end
                dma_state <= DMA_WAIT;
            end
            
            DMA_WRITE: begin
                if (channel_config[channel_select][0]) begin
                    // Memory to device
                    dev_select[channel_select] <= 1;
                    dev_data_out <= mem_data_in;
                    dev_write <= 1;
                end else begin
                    // Device to memory
                    mem_addr <= channel_addr[channel_select];
                    mem_data_out <= dev_data_in;
                    mem_write <= 1;
                end
                dma_state <= DMA_NEXT;
            end
            
            // ... Additional states for transfer completion
        endcase
    end
end

endmodule

Key Features

1. Multi-Channel Support

  • 4 independent DMA channels
  • Per-channel configuration
  • Bidirectional transfers
  • Channel priorities

2. Transfer Types

  • Memory to device
  • Device to memory
  • Memory to memory
  • I/O to memory

3. Control Features

  • Programmable addresses
  • Transfer count
  • Interrupt generation
  • Error handling

Magic-1 Memory Management Unit (MMU) Implementation

Overview

The MMU provides virtual memory support and memory protection for the Magic-1 architecture. This implementation follows the original hardware design while providing modern memory management features.

Core MMU Implementation

module magic1_mmu #(
    parameter VADDR_WIDTH = 16,    // Virtual address width
    parameter PADDR_WIDTH = 20,    // Physical address width
    parameter PAGE_SIZE = 1024     // 1KB pages
)(
    // Clock and control
    input  wire clk,
    input  wire reset_n,
    
    // CPU interface
    input  wire [VADDR_WIDTH-1:0] virtual_addr,
    input  wire cpu_read,
    input  wire cpu_write,
    input  wire cpu_fetch,
    input  wire [15:0] ptb_reg,    // Page Table Base Register
    
    // Memory interface
    output reg [PADDR_WIDTH-1:0] physical_addr,
    output reg mmu_fault,
    output reg protection_fault,
    output reg access_complete,
    
    // TLB interface
    output reg tlb_hit,
    output reg tlb_miss,
    output reg tlb_update
);

// Page table entry structure
typedef struct packed {
    logic valid;           // Valid entry
    logic writable;        // Write permission
    logic executable;      // Execute permission
    logic [9:0] pfn;      // Physical page frame number
} page_table_entry_t;

// TLB entry storage
page_table_entry_t tlb_entries[16];
reg [3:0] tlb_vpn[16];    // Virtual page numbers
reg [3:0] tlb_lru;        // LRU counter

// Page table walk state machine
reg [2:0] mmu_state;
localparam [2:0]
    MMU_IDLE = 3'b000,
    MMU_TLB_CHECK = 3'b001,
    MMU_PT_FETCH = 3'b010,
    MMU_UPDATE_TLB = 3'b011,
    MMU_COMPLETE = 3'b100;

// Extract page number and offset
wire [5:0] vpn = virtual_addr[15:10];
wire [9:0] offset = virtual_addr[9:0];

always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        mmu_state <= MMU_IDLE;
        mmu_fault <= 0;
        protection_fault <= 0;
        tlb_hit <= 0;
        tlb_miss <= 0;
    end else begin
        case (mmu_state)
            MMU_IDLE: begin
                if (cpu_read || cpu_write || cpu_fetch) begin
                    mmu_state <= MMU_TLB_CHECK;
                end
            end
            
            MMU_TLB_CHECK: begin
                tlb_hit <= 0;
                for (int i = 0; i < 16; i++) begin
                    if (tlb_vpn[i] == vpn && tlb_entries[i].valid) begin
                        tlb_hit <= 1;
                        physical_addr <= {tlb_entries[i].pfn, offset};
                        
                        // Check permissions
                        if (cpu_write && !tlb_entries[i].writable) begin
                            protection_fault <= 1;
                        end
                        if (cpu_fetch && !tlb_entries[i].executable) begin
                            protection_fault <= 1;
                        end
                        
                        mmu_state <= MMU_COMPLETE;
                    end
                end
                
                if (!tlb_hit) begin
                    tlb_miss <= 1;
                    mmu_state <= MMU_PT_FETCH;
                end
            end
            
            // Additional states for page table walk
            // ...existing code...
        endcase
    end
end

endmodule

Key Features

  1. Address Translation

    • 16-bit virtual addresses
    • 20-bit physical addresses
    • 1KB page size
    • Hardware TLB support
  2. Memory Protection

    • Read/Write/Execute permissions
    • Page-level protection
    • Permission fault detection
    • Privileged operations
  3. TLB Management

    • 16-entry TLB
    • LRU replacement
    • Hardware page table walk
    • TLB invalidation support

Magic-1 Cache Controller Implementation - Part 16

The Magic-1 cache controller implements a direct-mapped cache with write-through policy. Let's create the cache implementation.

Cache Controller Core

module magic1_cache_controller #(
    parameter CACHE_SIZE = 4096,    // 4KB cache
    parameter LINE_SIZE = 32,       // 32-byte cache lines
    parameter ADDR_WIDTH = 16
)(
    // Clock and control
    input  wire clk,
    input  wire reset_n,
    
    // CPU interface
    input  wire [ADDR_WIDTH-1:0] cpu_addr,
    input  wire [15:0] cpu_write_data,
    output reg  [15:0] cpu_read_data,
    input  wire cpu_read,
    input  wire cpu_write,
    output reg  cpu_ready,
    
    // Memory interface
    output reg  [ADDR_WIDTH-1:0] mem_addr,
    output reg  [15:0] mem_write_data,
    input  wire [15:0] mem_read_data,
    output reg  mem_read,
    output reg  mem_write,
    input  wire mem_ready,
    
    // Cache statistics
    output reg [31:0] cache_hits,
    output reg [31:0] cache_misses
);

// Cache organization
localparam LINES = CACHE_SIZE / LINE_SIZE;
localparam LINE_WORDS = LINE_SIZE / 2;
localparam TAG_BITS = ADDR_WIDTH - $clog2(LINES) - $clog2(LINE_WORDS);
localparam INDEX_BITS = $clog2(LINES);
localparam OFFSET_BITS = $clog2(LINE_WORDS);

// Cache storage
reg [15:0] cache_data[LINES][LINE_WORDS];
reg [TAG_BITS-1:0] cache_tags[LINES];
reg cache_valid[LINES];

// Address breakdown
wire [TAG_BITS-1:0] addr_tag = cpu_addr[ADDR_WIDTH-1:ADDR_WIDTH-TAG_BITS];
wire [INDEX_BITS-1:0] addr_index = cpu_addr[ADDR_WIDTH-TAG_BITS-1:OFFSET_BITS];
wire [OFFSET_BITS-1:0] addr_offset = cpu_addr[OFFSET_BITS-1:0];

// Cache state machine
reg [2:0] cache_state;
localparam [2:0]
    CACHE_IDLE = 3'b000,
    CACHE_READ = 3'b001,
    CACHE_WRITE = 3'b010,
    CACHE_FILL = 3'b011,
    CACHE_WAIT = 3'b100;

always @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        cache_state <= CACHE_IDLE;
        cpu_ready <= 1'b0;
        cache_hits <= 32'h0;
        cache_misses <= 32'h0;
        
        // Initialize cache valid bits
        for (int i = 0; i < LINES; i++) begin
            cache_valid[i] <= 1'b0;
        end
    end else begin
        case (cache_state)
            // ... Cache state machine implementation ...
        endcase
    end
end

endmodule

Key Features

1. Cache Organization

  • Direct-mapped cache
  • 4KB total size
  • 32-byte cache lines
  • Write-through policy

2. Cache Operations

  • Read handling
  • Write handling
  • Line filling
  • Cache coherency

3. Performance Monitoring

  • Hit/miss counters
  • Ready signaling
  • Memory interfacing
  • Line replacement

Magic-1 System Integration - Part 17

Integration Module

This module ties together all the core components of the Magic-1 computer system.

module magic1_system #(
    parameter CLOCK_FREQ = 50_000_000,
    parameter MEMORY_SIZE = 32768  // 32KB main memory
)(
    // External interface
    input  wire ext_clk,
    input  wire ext_reset_n,
    
    // Serial interface
    input  wire uart_rx,
    output wire uart_tx,
    
    // Front panel
    input  wire [15:0] fp_switches,
    output wire [15:0] fp_leds,
    input  wire [3:0]  fp_buttons,
    
    // Debug interface
    output wire debug_tx,
    input  wire debug_rx
);

    // Internal buses
    wire [15:0] l_bus;
    wire [15:0] r_bus;
    
    // Control signals
    wire [7:0] next_address;
    wire [3:0] register_latch;
    wire mar_latch, mdr_lo_latch, mdr_hi_latch;
    wire [3:0] alu_operation;
    wire [1:0] memory_control;

    // Clock and reset
    wire sys_clk, cpu_clk, mem_clk;
    wire sys_reset_n;

    // Instantiate clock generator
    magic1_clock_gen clock_gen (
        .ext_clk(ext_clk),
        .ext_reset_n(ext_reset_n),
        .sys_clk(sys_clk),
        .cpu_clk(cpu_clk),
        .mem_clk(mem_clk),
        .sys_reset_n(sys_reset_n)
    );

    // Instantiate ALU
    magic1_alu alu (
        .clk(cpu_clk),
        .reset_n(sys_reset_n),
        .l_bus(l_bus),
        .r_bus(r_bus),
        .op_code(alu_operation),
        .result(alu_result)
        // ...additional ports...
    );

    // Instantiate memory controller
    magic1_memory_controller memory (
        .clk(mem_clk),
        .reset_n(sys_reset_n),
        .address(mar_out),
        .write_data(mdr_out),
        .read_data(mem_data)
        // ...additional ports...
    );

    // Instantiate control unit
    magic1_control_unit control (
        .clk(cpu_clk),
        .reset_n(sys_reset_n),
        .instruction(ir_out),
        .next_address(next_address),
        .register_latch(register_latch)
        // ...additional ports...
    );

    // Instantiate front panel
    magic1_front_panel panel (
        .clk(sys_clk),
        .reset_n(sys_reset_n),
        .switches(fp_switches),
        .leds(fp_leds),
        .buttons(fp_buttons)
        // ...additional ports...
    );

    // Inter-module connections
    always @(*) begin
        // Bus routing logic
        // Control signal distribution
        // Status collection
    end

endmodule

Key Integration Points

  1. Clock Distribution

    • System clock
    • CPU clock
    • Memory clock
    • Peripheral clocks
  2. Bus Structure

    • Left bus (L-bus)
    • Right bus (R-bus)
    • Memory bus
    • I/O bus
  3. Control Flow

    • Microcode sequencing
    • Register control
    • Memory access
    • I/O operations

Magic-1 System Verification - Part 18

Test Bench Implementation

Let's create a comprehensive test bench for verifying the Magic-1 system functionality.

module magic1_system_tb;

    // Clock and reset generation
    reg ext_clk = 0;
    reg ext_reset_n = 0;
    
    // Test stimulus
    reg [15:0] fp_switches;
    reg [3:0] fp_buttons;
    reg uart_rx = 1;
    
    // Output monitoring
    wire [15:0] fp_leds;
    wire uart_tx;
    wire debug_tx;
    
    // Test program memory initialization
    reg [15:0] test_program [0:255];
    integer i;
    
    // Clock generation
    always #10 ext_clk = ~ext_clk;

    // DUT instantiation
    magic1_system #(
        .CLOCK_FREQ(50_000_000),
        .MEMORY_SIZE(32768)
    ) dut (
        .ext_clk(ext_clk),
        .ext_reset_n(ext_reset_n),
        .uart_rx(uart_rx),
        .uart_tx(uart_tx),
        .fp_switches(fp_switches),
        .fp_leds(fp_leds),
        .fp_buttons(fp_buttons),
        .debug_tx(debug_tx),
        .debug_rx(1'b1)
    );

    // Test sequences
    initial begin
        $dumpfile("magic1_test.vcd");
        $dumpvars(0, magic1_system_tb);
        
        // Initialize test program
        $readmemh("test_program.hex", test_program);
        
        // Reset sequence
        ext_reset_n = 0;
        fp_switches = 16'h0000;
        fp_buttons = 4'h0;
        #100;
        ext_reset_n = 1;
        
        // Load test program
        for (i = 0; i < 256; i++) begin
            load_memory(i, test_program[i]);
        end
        
        // Run test sequences
        test_alu_operations();
        test_memory_access();
        test_io_operations();
        
        $finish;
    end
    
    // Test task definitions
    task test_alu_operations;
        begin
            // Test ADD
            load_register(6'h0, 16'h1234);  // Load A
            load_register(6'h1, 16'h5678);  // Load B
            execute_instruction(8'h01);      // ADD A,B
            check_result(16'h68AC);
            
            // Test SUB
            // ...additional operations...
        end
    endtask
    
    // Helper tasks
    task load_memory;
        input [15:0] addr;
        input [15:0] data;
        begin
            @(posedge ext_clk);
            fp_switches = addr;
            fp_buttons[0] = 1;
            #20;
            fp_buttons[0] = 0;
            fp_switches = data;
            fp_buttons[1] = 1;
            #20;
            fp_buttons[1] = 0;
        end
    endtask

endmodule

Verification Components

  1. Test Scenarios

    • ALU operations
    • Memory access
    • I/O operations
    • Interrupt handling
    • Front panel functions
  2. Stimulus Generation

    • Test program loading
    • Register initialization
    • Interrupt injection
    • I/O simulation
  3. Response Checking

    • Result verification
    • Timing validation
    • Protocol checking
    • State monitoring

Magic-1 System Debug Features - Part 19

Core Debug Infrastructure

Let's implement a comprehensive debug system for the Magic-1 computer that provides real-time visibility and control.

Debug Controller Implementation

module magic1_debug_controller #(
    parameter TRACE_DEPTH = 1024
)(
    // System interface
    input wire clk,
    input wire reset_n,
    
    // CPU monitoring
    input wire [15:0] pc_value,
    input wire [15:0] instruction,
    input wire [15:0] reg_a,
    input wire [15:0] reg_b,
    input wire [15:0] reg_c,
    input wire [15:0] alu_result,
    
    // Debug control
    input wire debug_enable,
    input wire [7:0] debug_command,
    output reg breakpoint_hit,
    output reg trace_full,
    
    // UART interface
    input wire uart_rx,
    output reg uart_tx,
    
    // Trace memory interface
    output reg [15:0] trace_addr,
    output reg [63:0] trace_data,
    output reg trace_write
);

// Trace entry format
typedef struct packed {
    logic [15:0] pc;
    logic [15:0] instr;
    logic [15:0] reg_vals;
    logic [15:0] result;
} trace_entry_t;

// Debug command codes
localparam [7:0]
    CMD_NOP     = 8'h00,
    CMD_STEP    = 8'h01,
    CMD_CONT    = 8'h02,
    CMD_BP_SET  = 8'h03,
    CMD_BP_CLR  = 8'h04,
    CMD_TRACE   = 8'h05;

// Internal storage
trace_entry_t trace_buffer[TRACE_DEPTH];
reg [9:0] trace_ptr;
reg [15:0] breakpoints[4];
reg [3:0] bp_valid;

Debug Command Interface

Let's implement the debug command processor:

module magic1_debug_cmd (
    input wire clk,
    input wire reset_n,
    
    // Command interface
    input wire [7:0] cmd_data,
    input wire cmd_valid,
    output reg cmd_ready,
    
    // Response interface
    output reg [7:0] resp_data,
    output reg resp_valid,
    input wire resp_ready,
    
    // Debug control signals
    output reg [15:0] break_addr,
    output reg break_enable,
    output reg single_step,
    output reg trace_enable
);

// Command processor state machine
localparam [2:0]
    CMD_IDLE = 3'b000,
    CMD_DECODE = 3'b001,
    CMD_EXECUTE = 3'b010,
    CMD_RESPONSE = 3'b011;

Integration with Main System

Add these signals to the main Magic-1 system:

module magic1_system #(
    // ...existing code...
) (
    // ...existing code...
    
    // Debug interface
    input wire debug_enable,
    input wire [7:0] debug_command,
    output wire debug_active,
    output wire [7:0] debug_status,
    
    // Debug trace output
    output wire trace_valid,
    output wire [63:0] trace_data
);

Key Debug Features

  1. Hardware Breakpoints

    • Multiple breakpoint support
    • Address matching
    • Condition triggers
    • Break on access
  2. Instruction Tracing

    • Circular trace buffer
    • PC, instruction, registers
    • ALU results
    • Memory access
  3. Debug Commands

    • Single step
    • Run/halt control
    • Memory examination
    • Register inspection

Magic-1 System Performance Monitoring - Part 20

Performance Monitor Core

The performance monitoring system tracks key metrics of the Magic-1 CPU operation in real-time.

Performance Counter Module

module magic1_perf_monitor #(
    parameter NUM_COUNTERS = 16,
    parameter COUNTER_WIDTH = 32
)(
    // Clock and control
    input wire clk,
    input wire reset_n,
    
    // CPU event inputs
    input wire instruction_complete,
    input wire memory_read,
    input wire memory_write,
    input wire cache_hit,
    input wire cache_miss,
    input wire interrupt_active,
    
    // Counter interface
    input wire [3:0] counter_select,
    input wire counter_read,
    output reg [COUNTER_WIDTH-1:0] counter_value,
    
    // Performance data output
    output reg [31:0] ipc_rate,        // Instructions per cycle
    output reg [31:0] cache_hit_rate,  // Cache hit percentage
    output reg [31:0] bus_util         // Bus utilization
);

// Performance counter definitions
localparam [3:0]
    CNT_INSTRUCTIONS = 4'h0,
    CNT_MEM_READS   = 4'h1,
    CNT_MEM_WRITES  = 4'h2,
    CNT_CACHE_HITS  = 4'h3,
    CNT_CACHE_MISS  = 4'h4,
    CNT_INTERRUPTS  = 4'h5,
    CNT_CYCLES      = 4'h6;

// Counter storage
reg [COUNTER_WIDTH-1:0] counters[NUM_COUNTERS-1:0];
reg [31:0] cycle_counter;

Real-time Metrics Calculator

module magic1_metrics #(
    parameter WINDOW_SIZE = 1024
)(
    // Clock and control
    input wire clk,
    input wire reset_n,
    
    // Raw counter inputs
    input wire [31:0] instruction_count,
    input wire [31:0] cycle_count,
    input wire [31:0] cache_hits,
    input wire [31:0] cache_misses,
    
    // Calculated metrics
    output reg [31:0] ipc,           // Instructions per cycle (fixed point)
    output reg [31:0] cache_rate,    // Cache hit rate (percentage)
    output reg [31:0] stall_cycles,  // CPU stall cycles
    
    // Status flags
    output reg metrics_valid,
    output reg window_complete
);

System Integration Points

  1. Event Collection

    - Instruction completion
    - Memory operations
    - Cache activity
    - Interrupt handling
    - Bus transactions
  2. Performance Metrics

    - Instructions per cycle
    - Cache hit/miss ratio
    - Memory bandwidth
    - Interrupt latency
    - Bus utilization
  3. Data Presentation

    - Real-time updates
    - Cumulative statistics
    - Rolling averages
    - Peak detection

Magic-1 RTL Test Bench Implementation - Part 21

Let's create a comprehensive RTL test bench for verifying the Magic-1 computer system.

Main Test Bench Structure

module magic1_rtl_tb;

    // Clock generation
    reg clk = 0;
    reg reset_n = 0;
    
    // Test program storage
    reg [15:0] test_memory[0:4095];
    
    // Test stimulus and monitoring
    reg [15:0] test_input;
    wire [15:0] test_output;
    
    // Test bench control
    integer test_phase = 0;
    integer error_count = 0;
    
    // Clock generation - 50MHz
    always #10 clk = ~clk;
    
    // DUT instantiation
    magic1_system dut (
        .clk(clk),
        .reset_n(reset_n),
        .external_input(test_input),
        .external_output(test_output)
    );
    
    // Test execution
    initial begin
        // Load test program
        $readmemh("test_program.hex", test_memory);
        
        // Reset sequence
        reset_n = 0;
        #100;
        reset_n = 1;
        
        // Execute test phases
        run_instruction_tests();
        run_memory_tests();
        run_io_tests();
        
        // Report results
        $display("Test completed with %d errors", error_count);
        $finish;
    end

    // Helper tasks here...
endmodule

Instruction Test Task

    task run_instruction_tests;
        begin
            $display("Starting instruction tests");
            
            // Test ALU operations
            test_alu_add();
            test_alu_sub();
            test_alu_logic();
            
            // Test memory operations
            test_load_store();
            
            // Test branch operations
            test_conditional_branch();
            
            $display("Instruction tests complete");
        end
    endtask

Memory Access Test Task

    task run_memory_tests;
        begin
            // Test memory access patterns
            test_byte_access();
            test_word_access();
            test_block_transfer();
            
            // Test memory protection
            test_privileged_access();
        end
    endtask

Run the tests using:

cd /testbench
iverilog -o magic1_test magic1_rtl_tb.v ../rtl/*.v
vvp magic1_test

Magic-1 System Coverage Analysis - Part 22

Let's implement comprehensive coverage analysis for the Magic-1 CPU verification.

Functional Coverage Implementation

First, let's define our coverage points in SystemVerilog:

module magic1_coverage;

    // Coverage for instruction execution
    covergroup instruction_coverage;
        // Instruction type coverage
        OPCODE: coverpoint instruction[15:8] {
            bins alu_ops[] = {8'h00, 8'h01, 8'h02, 8'h03};
            bins mem_ops[] = {8'h10, 8'h11, 8'h12, 8'h13};
            bins branch_ops[] = {8'h20, 8'h21, 8'h22};
            bins system_ops[] = {8'hF0, 8'hF1, 8'hF2};
        }
        
        // Addressing modes
        ADDR_MODE: coverpoint instruction[7:6] {
            bins immediate = {2'b00};
            bins register = {2'b01};
            bins indirect = {2'b10};
            bins indexed = {2'b11};
        }
        
        // Register usage
        REG_USAGE: coverpoint instruction[5:3] {
            bins reg_a = {3'b110};
            bins reg_b = {3'b111};
            bins reg_c = {3'b010};
        }
    endgroup

    // Coverage for memory access patterns
    covergroup memory_coverage;
        // Address ranges
        ADDRESS: coverpoint memory_address {
            bins low = {[0:16'h3FFF]};
            bins mid = {[16'h4000:16'h7FFF]};
            bins high = {[16'h8000:16'hFFFF]};
        }
        
        // Access types
        ACCESS: coverpoint {memory_read, memory_write} {
            bins read = {2'b10};
            bins write = {2'b01};
            bins rmw = {2'b11};
        }
    endgroup

endmodule

Test Coverage Implementation

Let's create a comprehensive test suite:

module magic1_test_suite;
    // Test configuration
    magic1_config cfg;
    
    // Coverage collection
    instruction_coverage instr_cov;
    memory_coverage mem_cov;
    
    // Test sequences
    task run_test_suite();
        // Initialize coverage
        instr_cov = new();
        mem_cov = new();
        
        // Run test sequences
        test_alu_operations();
        test_memory_operations();
        test_branching();
        test_interrupts();
        
        // Report coverage
        $display("Instruction coverage: %0.2f%%", instr_cov.get_coverage());
        $display("Memory coverage: %0.2f%%", mem_cov.get_coverage());
    endtask
    
    // Helper tasks for individual test categories
    task test_alu_operations();
        // Test all ALU operations
        // ...implementation...
    endtask
    
    // Additional test tasks
    // ...implementation...
    
endmodule

To run the coverage analysis:

cd /testbench
vlog magic1_coverage.sv magic1_test_suite.sv
vsim -c magic1_test_suite -do "run -all; coverage report -detail"

The coverage report will show:

  • Instruction coverage metrics
  • Memory access patterns
  • State transitions
  • Exception handling

Type 'next' for Part 23 covering System Documentation.

Magic-1 System Testing and Documentation - Part 23

Test Documentation Structure

Let's create a comprehensive test suite documentation in Markdown format with associated test code.

1. Test Overview Document

# Magic-1 Test Suite Documentation

## Test Categories
1. Core CPU Tests
   - ALU Operations
   - Register Operations
   - Memory Access
   - Instruction Decoding

2. System Tests
   - Boot Sequence
   - Interrupt Handling
   - I/O Operations
   - Memory Management

## Running Tests
```bash
cd /testbench
make test       # Run all tests
make test-cpu   # Run CPU tests only
make test-io    # Run I/O tests only
```

2. Test Implementation

module magic1_test_suite;
    // Test configuration parameters
    parameter TEST_TIMEOUT = 10000;
    parameter VERBOSE = 1;

    // Test categories
    typedef enum {
        TEST_ALU,
        TEST_MEMORY,
        TEST_IO,
        TEST_SYSTEM
    } test_category_t;

    // Test result structure
    typedef struct {
        string test_name;
        bit passed;
        int errors;
        time duration;
    } test_result_t;

    // Test tracking
    test_result_t results[$];
    
    task run_cpu_tests();
        test_result_t result;
        
        $display("Starting CPU Test Suite");
        
        // ALU Tests
        result.test_name = "ALU_ADD";
        test_alu_add(result);
        results.push_back(result);
        
        // Additional tests...
        
        print_results();
    endtask

    // Helper tasks
    task test_alu_add(output test_result_t result);
        // Test implementation
    endtask
endmodule

3. Test Results Formatter

module magic1_test_report;
    // Report generation
    function void generate_test_report(test_result_t results[$]);
        int fd;
        fd = $fopen("test_results.md", "w");
        
        $fwrite(fd, "# Magic-1 Test Results\n\n");
        $fwrite(fd, "| Test Name | Status | Errors | Duration |\n");
        $fwrite(fd, "|-----------|--------|---------|----------|\n");
        
        foreach (results[i]) begin
            $fwrite(fd, "|%s|%s|%d|%t|\n", 
                results[i].test_name,
                results[i].passed ? "PASS" : "FAIL",
                results[i].errors,
                results[i].duration);
        end
        
        $fclose(fd);
    endfunction
endmodule

Magic-1 System Integration Testing - Part 24

Integration Test Framework

Test Environment Setup

Create a comprehensive integration test environment for the Magic-1 system:

module magic1_integration_tb;
    // System configuration
    parameter SYS_CLK_FREQ = 50_000_000;  // 50MHz
    parameter UART_BAUD = 9600;
    
    // Clock and reset
    reg sys_clk;
    reg reset_n;
    
    // System interfaces
    reg uart_rx;
    wire uart_tx;
    reg [15:0] gpio_in;
    wire [15:0] gpio_out;
    
    // Test control
    event test_done;
    integer test_errors;
    
    // Clock generation
    initial begin
        sys_clk = 0;
        forever #10 sys_clk = ~sys_clk;
    end
    
    // DUT instantiation
    magic1_system DUT (
        .sys_clk(sys_clk),
        .reset_n(reset_n),
        .uart_rx(uart_rx),
        .uart_tx(uart_tx),
        .gpio_in(gpio_in),
        .gpio_out(gpio_out)
    );
    
    // Test sequence control
    initial begin
        run_integration_tests();
        -> test_done;
    end
endmodule

Test Scenarios Implementation

Create dedicated test scenarios:

module integration_tests;
    // Test vectors
    typedef struct {
        string name;
        logic [15:0] stimulus;
        logic [15:0] expected;
        time delay;
    } test_vector_t;
    
    // Test sequence definitions
    test_vector_t uart_test_vectors[] = '{
        '{"UART_SEND", 16'h55AA, 16'h55AA, 100},
        '{"UART_RECV", 16'hAA55, 16'hAA55, 100}
    };
    
    task run_uart_tests;
        foreach (uart_test_vectors[i]) begin
            test_uart_transfer(uart_test_vectors[i]);
        end
    endtask
    
    // Additional test tasks...
endmodule

System Test Runner

Create a main test runner:

#!/bin/bash
# filepath: /scripts/run_integration_tests.sh

# Compile test environment
iverilog -o magic1_integration \
    ../testbench/magic1_integration_tb.v \
    ../testbench/integration_tests.v \
    ../rtl/*.v

# Run tests
./magic1_integration

# Generate report
python3 generate_test_report.py test_results.log

Magic-1 System Performance Testing - Part 25

Performance Test Framework

Let's create a comprehensive performance testing framework for the Magic-1 system.

Core Performance Test Module

module magic1_perf_tb;
    // Performance test parameters
    parameter TEST_CYCLES = 1000000;
    parameter WARMUP_CYCLES = 10000;
    
    // Performance metrics
    int instructions_executed;
    int cache_hits;
    int cache_misses;
    time test_start_time;
    time test_end_time;
    
    // Test control signals
    reg perf_test_active;
    reg collect_stats;
    
    // Performance counters
    always @(posedge sys_clk) begin
        if (collect_stats) begin
            if (DUT.instruction_complete)
                instructions_executed++;
            if (DUT.cache_hit)
                cache_hits++;
            if (DUT.cache_miss)
                cache_misses++;
        end
    end
    
    // Test execution task
    task run_performance_test;
        begin
            $display("Starting performance test...");
            
            // Warmup phase
            perf_test_active = 1;
            collect_stats = 0;
            repeat(WARMUP_CYCLES) @(posedge sys_clk);
            
            // Collection phase
            test_start_time = $time;
            collect_stats = 1;
            repeat(TEST_CYCLES) @(posedge sys_clk);
            test_end_time = $time;
            collect_stats = 0;
            
            report_performance();
        end
    endtask
endmodule

Performance Test Runner

Create a shell script to run performance tests:

#!/bin/bash

# Compile performance test
iverilog \
    -o magic1_perf \
    ../testbench/magic1_perf_tb.v \
    ../rtl/*.v

# Run different test scenarios
for benchmark in dhrystone coremark linpack; do
    echo "Running $benchmark benchmark..."
    ./magic1_perf -b $benchmark
done

# Generate performance report
python3 generate_perf_report.py perf_results.log

Performance Test Report Generator

import sys
import json
from datetime import datetime

def generate_report(logfile):
    with open(logfile, 'r') as f:
        results = json.load(f)
    
    report = f"""# Magic-1 Performance Test Report
Generated: {datetime.now()}

## Test Results

| Benchmark | IPC | Cache Hit Rate | MIPS |
|-----------|-----|----------------|------|
"""
    
    for test in results:
        report += f"| {test['name']} | {test['ipc']:.2f} | {test['cache_hit_rate']:.1f}% | {test['mips']:.2f} |\n"
    
    with open('perf_report.md', 'w') as f:
        f.write(report)

if __name__ == '__main__':
    generate_report(sys.argv[1])

Magic-1 System Stress Testing - Part 26

Core Stress Test Framework

Let's implement a comprehensive stress testing framework for the Magic-1 system. This will exercise all system components under heavy load conditions.

Stress Test Controller

module magic1_stress_tb;
    // Stress test configuration
    parameter STRESS_DURATION = 1000000;  // 1M cycles
    parameter NUM_INTERRUPTS = 32;
    parameter MEMORY_STRESS_RATE = 80;    // 80% memory activity
    
    // Test control
    reg [31:0] stress_cycle_count;
    reg [7:0] active_interrupts;
    reg memory_stress_active;
    
    // Stress generators
    task generate_memory_stress;
        forever begin
            if (memory_stress_active && $urandom_range(100) < MEMORY_STRESS_RATE) begin
                // Generate random memory access
                memory_access($urandom_range('hFFFF), $urandom_range(1));
            end
            @(posedge clk);
        end
    endtask
    
    // Interrupt storm generator
    task generate_interrupt_storm;
        forever begin
            // Randomly toggle interrupts
            active_interrupts = $urandom;
            DUT.interrupt_controller.interrupt_requests = active_interrupts;
            repeat($urandom_range(10,100)) @(posedge clk);
        end
    endtask
endmodule

System Monitor

module magic1_stress_monitor;
    // System health metrics
    typedef struct {
        int deadlock_count;
        int timeout_count;
        int error_count;
        time max_response_time;
        time min_response_time;
    } health_metrics_t;
    
    health_metrics_t system_health;
    
    // Monitor task
    task monitor_system_health;
        forever begin
            // Check for deadlocks
            if (detect_deadlock()) begin
                system_health.deadlock_count++;
                log_system_state();
            end
            
            // Check response times
            measure_response_times();
            
            @(posedge clk);
        end
    endtask
endmodule

Test Control Script

#!/bin/bash

# Compile stress test environment
iverilog -g2012 \
    -o magic1_stress \
    ../testbench/magic1_stress_tb.v \
    ../testbench/magic1_stress_monitor.v \
    ../rtl/*.v

# Run stress tests with different configurations
for config in baseline memory_intensive interrupt_storm combined; do
    echo "Running $config stress test..."
    ./magic1_stress -c $config -d 1000000
    
    # Check for system failures
    if [ $? -ne 0 ]; then
        echo "Stress test failed: $config"
        exit 1
    fi
done

# Generate stress test report
python3 ../scripts/generate_stress_report.py

Magic-1 CPU Error Recovery Testing - Part 27

Error Recovery Test Framework

Create a comprehensive test framework to verify system error detection and recovery mechanisms.

Core Error Injection Module

module magic1_error_inject (
    input wire clk,
    input wire reset_n,
    
    // Error injection control
    input wire [3:0] error_type,
    input wire error_enable,
    
    // System interfaces to corrupt
    inout wire [15:0] system_bus,
    inout wire [15:0] memory_bus,
    output reg parity_error,
    output reg bus_timeout
);

    // Error type definitions
    localparam [3:0]
        ERR_NONE     = 4'h0,
        ERR_PARITY   = 4'h1,
        ERR_TIMEOUT  = 4'h2,
        ERR_BUS      = 4'h3,
        ERR_ADDRESS  = 4'h4;

    // Error injection logic
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            parity_error <= 1'b0;
            bus_timeout <= 1'b0;
        end else if (error_enable) begin
            case (error_type)
                ERR_PARITY:  parity_error <= 1'b1;
                ERR_TIMEOUT: bus_timeout <= 1'b1;
                ERR_BUS:     system_bus <= 16'hZZZZ;
                ERR_ADDRESS: memory_bus <= 16'hFFFF;
            endcase
        end
    end
endmodule

Error Recovery Test Sequence

module magic1_recovery_test;
    // Test configuration
    parameter NUM_ERROR_TESTS = 100;
    parameter ERROR_INTERVAL = 1000;  // cycles between errors
    
    // Error tracking
    int recovery_success;
    int recovery_failure;
    time recovery_time;
    
    task run_error_recovery_tests;
        for (int i = 0; i < NUM_ERROR_TESTS; i++) begin
            // Inject random error
            inject_random_error();
            
            // Monitor recovery
            if (wait_for_recovery(1000)) begin
                recovery_success++;
                record_recovery_time();
            end else begin
                recovery_failure++;
                log_recovery_failure();
            end
            
            // Wait before next error
            repeat(ERROR_INTERVAL) @(posedge clk);
        end
        
        generate_recovery_report();
    endtask
endmodule

Test Script

Run the error recovery tests using this shell script:

#!/bin/bash
# filepath: /scripts/test_error_recovery.sh

# Compile test environment
iverilog -o magic1_recovery \
    ../testbench/magic1_error_inject.v \
    ../testbench/magic1_recovery_test.v \
    ../rtl/*.v

# Run error injection tests
./magic1_recovery -test all

# Generate report
python3 generate_recovery_report.py test_results.log

Magic-1 Memory Subsystem Testing - Part 28

The memory subsystem requires rigorous testing to ensure reliability. Let's create a comprehensive test framework.

Memory Test Module

Create the core memory test module:

module magic1_memory_test #(
    parameter MEM_SIZE = 32768,  // 32KB
    parameter PAGE_SIZE = 1024   // 1KB pages
)(
    input  wire clk,
    input  wire reset_n,
    
    // Memory interface
    output reg [15:0] mem_addr,
    output reg [15:0] mem_wdata,
    input  wire [15:0] mem_rdata,
    output reg mem_write,
    output reg mem_read,
    input  wire mem_ready
);

    // Test patterns
    typedef enum {
        PAT_WALKING_1,
        PAT_WALKING_0,
        PAT_CHECKERBOARD,
        PAT_RANDOM
    } pattern_t;

    // Test statistics
    int errors_detected;
    int tests_completed;
    time test_start, test_end;

    // Memory test patterns
    task run_memory_test;
        begin
            $display("Starting memory subsystem test");
            test_start = $time;
            
            // Run test patterns
            test_pattern(PAT_WALKING_1);
            test_pattern(PAT_WALKING_0);
            test_pattern(PAT_CHECKERBOARD);
            test_pattern(PAT_RANDOM);
            
            test_end = $time;
            report_results();
        end
    endtask
endmodule

Memory Pattern Generator

Create a module to generate test patterns:

module memory_patterns;
    // Pattern generation functions
    function [15:0] get_pattern;
        input pattern_t pat_type;
        input [15:0] addr;
        begin
            case(pat_type)
                PAT_WALKING_1:  return (1 << (addr & 15));
                PAT_WALKING_0:  return ~(1 << (addr & 15));
                PAT_CHECKERBOARD: return (addr & 1) ? 16'hAAAA : 16'h5555;
                PAT_RANDOM:     return $random;
                default:        return 16'h0000;
            endcase
        end
    endfunction
endmodule

Test Runner Script

Create a script to run the memory tests:

#!/bin/bash

# Compile memory test modules
iverilog -g2012 \
    -o memory_test \
    ../testbench/magic1_memory_test.v \
    ../testbench/memory_patterns.v \
    ../rtl/memory_controller.v

# Run memory tests
./memory_test

# Generate test report
python3 generate_memory_report.py test_results.log

Run the tests from the terminal:

cd /scripts
chmod +x run_memory_tests.sh
./run_memory_tests.sh

Magic-1 Cache Controller Testing - Part 29

Overview

Let's implement comprehensive testing for the Magic-1 cache controller. This includes unit tests, performance measurements, and cache coherency verification.

Cache Test Implementation

Create a new test module for cache verification:

module magic1_cache_test #(
    parameter CACHE_SIZE = 4096,   // 4KB cache
    parameter LINE_SIZE = 32,      // 32-byte lines
    parameter WAYS = 2             // 2-way set associative
)(
    input  wire clk,
    input  wire reset_n,
    
    // Cache interface
    output reg [15:0] cpu_addr,
    output reg [15:0] cpu_wdata,
    input  wire [15:0] cpu_rdata,
    output reg cpu_read,
    output reg cpu_write,
    input  wire cache_hit,
    input  wire cache_miss
);

    // Test metrics
    int hit_count;
    int miss_count;
    int access_count;
    time test_start;
    time test_end;

    // Cache test patterns
    typedef enum {
        SEQ_ACCESS,    // Sequential access
        RANDOM_ACCESS, // Random access  
        THRASH_ACCESS  // Cache thrashing
    } access_pattern_t;

    // Test execution
    initial begin
        test_start = $time;
        
        // Run test sequences
        test_sequential_access();
        test_random_access();
        test_cache_thrashing();
        
        test_end = $time;
        report_cache_metrics();
    end
endmodule

Test Helper Functions

Add cache test utilities:

module cache_test_utils;
    // Cache access patterns
    task test_sequential_access;
        integer i;
        begin
            for (i = 0; i < CACHE_SIZE; i += LINE_SIZE) begin
                perform_cache_access(i, 1'b0); // Read
                track_cache_stats();
            end
        end
    endtask

    task track_cache_stats;
        begin
            access_count++;
            if (cache_hit)
                hit_count++;
            else
                miss_count++;
        end
    endtask
endmodule

Test Runner Script

Create a script to run the cache tests:

#!/bin/bash

# Compile cache test environment
iverilog -g2012 \
    -o cache_test \
    ../testbench/magic1_cache_test.v \
    ../testbench/cache_test_utils.v \
    ../rtl/cache_controller.v

# Run cache tests with different configurations
for config in direct_mapped two_way four_way; do
    echo "Testing $config configuration..."
    ./cache_test -c $config
done

# Generate cache performance report
python3 generate_cache_report.py results.log

Run the cache tests using:

cd /scripts
chmod +x run_cache_tests.sh
./run_cache_tests.sh

Magic-1 Pipeline Testing - Part 30

The pipeline testing framework verifies correct instruction flow and hazard handling in the Magic-1 CPU.

Pipeline Test Module

module magic1_pipeline_test (
    input wire clk,
    input wire reset_n,
    
    // Pipeline stage monitoring
    input wire [15:0] fetch_instr,
    input wire [15:0] decode_instr,
    input wire [15:0] execute_instr,
    input wire [15:0] memory_instr,
    input wire [15:0] writeback_instr,
    
    // Pipeline control signals
    input wire pipeline_stall,
    input wire pipeline_flush,
    
    // Test control
    output reg test_complete,
    output reg [31:0] test_errors
);

    // Pipeline tracking
    reg [15:0] instr_queue[$];  // Track instructions in flight
    reg [31:0] stall_count;
    reg [31:0] flush_count;
    
    // Monitor pipeline flow
    always @(posedge clk) begin
        if (reset_n) begin
            // Track instruction progression
            check_pipeline_progression();
            
            // Monitor hazards
            if (pipeline_stall)
                stall_count++;
            if (pipeline_flush)
                flush_count++;
        end
    end
endmodule

Pipeline Test Cases

module pipeline_test_cases;
    // Test sequence definitions
    typedef struct {
        string name;
        reg [15:0] instructions[];
        int expected_stalls;
        int expected_flushes;
    } test_case_t;
    
    test_case_t test_cases[] = '{
        '{
            name: "RAW_Hazard",
            instructions: '{
                16'h1234,  // ADD R1, R2
                16'h5678   // SUB R3, R1
            },
            expected_stalls: 2,
            expected_flushes: 0
        },
        // ...more test cases...
    };
endmodule

Running Pipeline Tests

Create a test script:

# Run from project root
cd /testbench
iverilog -g2012 \
    magic1_pipeline_test.v \
    pipeline_test_cases.v \
    ../rtl/*.v \
    -o pipeline_test

# Execute tests
./pipeline_test

Monitor test output in VS Code's integrated terminal or redirect to a file:

./pipeline_test > pipeline_results.txt

Magic-1 Instruction Set Testing - Part 31

Let's create a comprehensive test framework for validating the Magic-1 instruction set implementation.

Test Framework Structure

First, create the main instruction test module:

module magic1_instruction_test;
    // Test configuration
    parameter NUM_TEST_CASES = 1000;
    parameter TIMEOUT_CYCLES = 1000;
    
    // Instruction categories
    typedef enum {
        ALU_OPS,
        MEMORY_OPS,
        BRANCH_OPS,
        SYSTEM_OPS
    } instr_category_t;
    
    // Test metrics
    struct packed {
        int total_tests;
        int passed;
        int failed;
        int timeout;
    } test_stats;
    
    // Test execution
    initial begin
        $display("Starting Magic-1 Instruction Set Testing");
        test_alu_instructions();
        test_memory_instructions();
        test_branch_instructions();
        generate_test_report();
    end
endmodule

Create test vector definitions:

package instruction_vectors;
    typedef struct packed {
        logic [15:0] opcode;
        logic [15:0] operands;
        logic [15:0] expected_result;
        logic [3:0]  expected_flags;
    } test_vector_t;
    
    test_vector_t alu_test_vectors[] = '{
        '{16'h0001, 16'h1234, 16'h1235, 4'b0000}, // ADD
        '{16'h0002, 16'h5678, 16'h5677, 4'b0000}, // SUB
        '{16'h0003, 16'hFFFF, 16'h0000, 4'b0100}  // AND
    };
endpackage

Create a test runner script:

#!/bin/bash
# filepath: /scripts/run_instruction_tests.sh

echo "Running Magic-1 Instruction Tests"

# Compile test environment
iverilog -g2012 \
    -I../testbench \
    -o instruction_test \
    ../testbench/magic1_instruction_test.v \
    ../testbench/instruction_vectors.sv \
    ../rtl/*.v

# Run tests
./instruction_test

# Process results
python3 ../scripts/process_test_results.py test_output.log

Run tests from VS Code's integrated terminal:

cd /scripts
chmod +x run_instruction_tests.sh
./run_instruction_tests.sh

The test results will appear in VS Code's output pane.

Magic-1 Microcode Testing Framework - Part 32

Let's create a comprehensive test framework for verifying the Magic-1 microcode implementation.

Microcode Test Module Implementation

Create the core microcode test module:

module magic1_microcode_test;

    // Microinstruction structure
    typedef struct packed {
        logic [7:0]  next;         // Next address
        logic [3:0]  register_latch;
        logic        mar_latch;
        logic        mdr_lo_latch;
        logic        mdr_hi_latch;
        logic [3:0]  misc_control;
        logic [3:0]  l_bus_enable;
        logic [1:0]  r_bus_enable;
        logic [1:0]  immediate;
        logic [3:0]  alu_op;
        logic [1:0]  memory_control;
        logic [1:0]  page_control;
    } microinstruction_t;

    // Test case definition
    typedef struct {
        string name;
        logic [15:0] instruction;
        microinstruction_t expected_sequence[$];
        int max_cycles;
    } microcode_test_t;

    // Test execution statistics
    int tests_passed;
    int tests_failed;
    time test_start_time;
    time test_end_time;

endmodule

Test Vector Definition

Create a file containing microcode test vectors:

package microcode_vectors;

    import magic1_types::*;  // Import shared type definitions

    // Test vector array
    microcode_test_t test_vectors[] = '{
        '{
            name: "ADD_REG",
            instruction: 16'h0001,
            expected_sequence: '{
                '{next: 8'h01, register_latch: 4'h1, mar_latch: 1'b0, ...},
                '{next: 8'h02, register_latch: 4'h2, mar_latch: 1'b0, ...}
            },
            max_cycles: 4
        },
        // Additional test vectors...
    };

endpackage

Running Tests

Create a test runner script:

#!/bin/bash
# filepath: /scripts/test_microcode.sh

# Compile microcode tests
iverilog -g2012 \
    -I../include \
    -o microcode_test \
    ../testbench/magic1_microcode_test.v \
    ../testbench/microcode_vectors.sv \
    ../rtl/*.v

# Run tests
./microcode_test

# Generate report
python3 ../scripts/generate_microcode_report.py test_results.log

Run the tests from VS Code's integrated terminal:

cd /scripts
chmod +x test_microcode.sh
./test_microcode.sh

The test results will appear in VS Code's output pane.

Magic-1 Interrupt Testing Framework - Part 33

Let's create a comprehensive testing framework for the Magic-1 interrupt system.

Core Interrupt Test Module

First, let's create the main test module:

module magic1_interrupt_test;
    // Interrupt test parameters
    parameter NUM_INTERRUPTS = 8;
    parameter TEST_DURATION = 10000;  // cycles
    
    // Interrupt simulation signals
    reg [NUM_INTERRUPTS-1:0] interrupt_sources;
    reg [NUM_INTERRUPTS-1:0] interrupt_masks;
    reg interrupt_enable;
    
    // Test metrics
    struct packed {
        int interrupts_generated;
        int interrupts_serviced;
        int max_latency;
        int min_latency;
        int timeouts;
    } test_stats;
    
    // Test control
    event test_complete;
    reg test_failed;
    
    // Initialize test environment
    initial begin
        $display("Starting interrupt system test...");
        initialize_interrupts();
        run_interrupt_tests();
        -> test_complete;
    end
endmodule

Interrupt Test Sequence Generator

Create a module to generate test sequences:

module interrupt_sequence_generator;
    // Sequence generation parameters
    localparam MAX_SEQUENCES = 32;
    
    typedef struct packed {
        reg [7:0] interrupt_pattern;
        int delay_cycles;
        reg [7:0] expected_response;
    } interrupt_sequence_t;
    
    // Generate test sequences
    function interrupt_sequence_t[MAX_SEQUENCES-1:0] generate_sequences;
        // ...sequence generation logic...
    endfunction
endmodule

Test Runner Script

Create a bash script to run the interrupt tests:

#!/bin/bash

echo "Running Magic-1 Interrupt Tests"

# Compile test environment
iverilog -g2012 \
    -I../include \
    -o interrupt_test \
    ../testbench/magic1_interrupt_test.v \
    ../testbench/interrupt_sequences.v \
    ../rtl/*.v

# Run different test scenarios
for scenario in basic priority nested masked; do
    echo "Testing $scenario interrupt handling..."
    ./interrupt_test -scenario $scenario
done

# Generate test report
python3 ../scripts/process_interrupt_results.py test_output.log

Run the tests from VS Code's integrated terminal:

cd /scripts
chmod +x test_interrupts.sh
./test_interrupts.sh

The test results will appear in VS Code's output pane.

⚠️ **GitHub.com Fallback** ⚠️