📖 第5课:一维CA硬件引擎

流水线 SRAM接口 VGA显示 完整SoC

🎯 本课目标

将前4课的理论整合为一个完整的硬件引擎:支持任意Wolfram规则、多种边界条件、SRAM存储大网格、VGA实时显示,以及多步流水线加速。

🏗️ 系统架构

一个完整的一维CA硬件引擎需要解决以下问题:

核心挑战

顶层架构

┌──────────────────────────────────────────────────┐
│              CA Engine SoC                        │
│                                                   │
│  ┌─────────┐  ┌──────────┐  ┌──────────────┐    │
│  │ Control │  │ Rule LUT │  │ Boundary     │    │
│  │   FSM   │  │ (8-bit)  │  │ Handler      │    │
│  └────┬────┘  └────┬─────┘  └──────┬───────┘    │
│       │            │               │             │
│  ┌────▼────────────▼───────────────▼─────────┐   │
│  │         CA Compute Core                    │   │
│  │  ┌──────┐  ┌──────┐  ┌──────┐            │   │
│  │  │Cell 0│  │Cell 1│..│Cell N│            │   │
│  │  └──────┘  └──────┘  └──────┘            │   │
│  └──────────────┬───────────────────────────┘   │
│                 │                                │
│  ┌──────────────▼───────────────────────────┐   │
│  │         Dual-Port SRAM Controller         │   │
│  │  Bank A (curr)  │  Bank B (next)         │   │
│  └──────────────┬───────────────────────────┘   │
│                 │                                │
│  ┌──────────────▼───────────────────────────┐   │
│  │         VGA Display Controller            │   │
│  └──────────────────────────────────────────┘   │
└──────────────────────────────────────────────────┘

⚡ 寄存器版本:小型CA引擎

对于小网格(WIDTH ≤ 256),可以直接用寄存器存储状态,所有元胞并行计算。

完整一维CA引擎(寄存器版本)

// ============================================================================
// ca_engine_1d.v - 完整一维CA硬件引擎
// 支持:任意Wolfram规则、多种边界、步进/连续运行、状态导出
// ============================================================================
module ca_engine_1d #(
    parameter WIDTH = 128            // 网格宽度
)(
    input  wire             clk,
    input  wire             rst_n,

    // ---- 控制接口 ----
    input  wire             cmd_init,    // 初始化命令
    input  wire             cmd_step,    // 单步命令
    input  wire             cmd_run,     // 连续运行命令
    input  wire             cmd_halt,    // 停止命令
    input  wire [7:0]       rule,        // Wolfram规则号
    input  wire [1:0]       boundary,    // 00=环形 01=固定0 10=固定1 11=镜像
    input  wire [WIDTH-1:0] seed,        // 初始种子

    // ---- 状态输出 ----
    output wire [WIDTH-1:0] state_out,   // 当前状态
    output wire [31:0]      step_count,  // 步数
    output wire             running,     // 正在运行标志
    output wire             step_done    // 单步完成脉冲
);

    // ---- 状态机 ----
    localparam S_IDLE = 2'd0;
    localparam S_INIT = 2'd1;
    localparam S_STEP = 2'd2;
    localparam S_RUN  = 2'd3;

    reg [1:0] fsm_state;
    reg [WIDTH-1:0] curr;
    reg [31:0] steps;

    // ---- 规则查找(组合逻辑) ----
    wire [WIDTH-1:0] nxt;

    // 边界值计算
    wire left_edge, right_edge;
    always @(*) begin
        case (boundary)
            2'b00: begin  // 环形
                left_edge  = curr[WIDTH-1];
                right_edge = curr[0];
            end
            2'b01: begin  // 固定0
                left_edge  = 1'b0;
                right_edge = 1'b0;
            end
            2'b10: begin  // 固定1
                left_edge  = 1'b1;
                right_edge = 1'b1;
            end
            2'b11: begin  // 镜像
                left_edge  = curr[0];
                right_edge = curr[WIDTH-1];
            end
            default: begin
                left_edge  = curr[WIDTH-1];
                right_edge = curr[0];
            end
        endcase
    end

    // 每个元胞的规则查找
    genvar i;
    generate
        for (i = 0; i < WIDTH; i = i + 1) begin : gen_compute
            wire L = (i == 0)        ? left_edge  : curr[i-1];
            wire C = curr[i];
            wire R = (i == WIDTH-1)  ? right_edge : curr[i+1];
            wire [2:0] nb = {L, C, R};
            assign nxt[i] = rule[nb];
        end
    endgenerate

    // ---- 状态机逻辑 ----
    reg step_done_r;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            fsm_state   <= S_IDLE;
            curr        <= {WIDTH{1'b0}};
            steps       <= 32'd0;
            step_done_r <= 1'b0;
        end else begin
            step_done_r <= 1'b0;  // 默认清除
            case (fsm_state)
                S_IDLE: begin
                    if (cmd_init) begin
                        fsm_state <= S_INIT;
                    end else if (cmd_step) begin
                        fsm_state <= S_STEP;
                    end else if (cmd_run) begin
                        fsm_state <= S_RUN;
                    end
                end
                S_INIT: begin
                    curr        <= seed;
                    steps       <= 32'd0;
                    fsm_state   <= S_IDLE;
                    step_done_r <= 1'b1;
                end
                S_STEP: begin
                    curr        <= nxt;
                    steps       <= steps + 32'd1;
                    fsm_state   <= S_IDLE;
                    step_done_r <= 1'b1;
                end
                S_RUN: begin
                    if (cmd_halt) begin
                        fsm_state <= S_IDLE;
                    end else begin
                        curr  <= nxt;
                        steps <= steps + 32'd1;
                    end
                end
                default: fsm_state <= S_IDLE;
            endcase
        end
    end

    assign state_out = curr;
    assign step_count = steps;
    assign running = (fsm_state == S_RUN);
    assign step_done = step_done_r;

endmodule

💾 SRAM版本:大网格CA引擎

对于大网格(WIDTH = 1024~65536),需要使用外部SRAM。设计要点是时分复用——每周期从SRAM读取3个邻域值,计算下一状态,写回。

SRAM大网格CA引擎

// ============================================================================
// ca_engine_sram.v - 基于SRAM的大网格一维CA引擎
// 使用双Bank SRAM实现双缓冲,逐元胞顺序处理
// ============================================================================
module ca_engine_sram #(
    parameter ADDR_W = 16,         // 地址位宽(最大65536个元胞)
    parameter SRAM_W = 8,          // SRAM数据位宽(8元胞/字)
    parameter CELLS  = 65536       // 总元胞数
)(
    input  wire              clk,
    input  wire              rst_n,
    input  wire              start,       // 开始运行
    input  wire              init,        // 初始化
    input  wire [7:0]        rule,        // Wolfram规则
    output wire              busy,        // 引擎忙
    output wire              frame_done,  // 一帧(全网格更新)完成

    // SRAM接口(双Bank)
    output wire [ADDR_W-1:0] sram_a_addr,
    output wire [SRAM_W-1:0] sram_a_dout,
    input  wire [SRAM_W-1:0] sram_a_din,
    output wire              sram_a_we,
    output wire [ADDR_W-1:0] sram_b_addr,
    output wire [SRAM_W-1:0] sram_b_dout,
    input  wire [SRAM_W-1:0] sram_b_din,
    output wire              sram_b_we
);

    // ---- 计数器 ----
    localparam TOTAL_WORDS = CELLS / SRAM_W;
    reg [ADDR_W-1:0] word_cnt;
    reg [2:0]        bit_cnt;     // SRAM字内偏移
    reg [1:0]        phase;       // 0=读1 1=读2 2=计算+写 3=完成

    // ---- 移位寄存器(3位邻域) ----
    reg [2:0] neighborhood;

    // ---- 下一状态缓冲 ----
    reg [SRAM_W-1:0] write_buf;

    // ---- 帧完成标志 ----
    reg frame_done_r;

    // ---- SRAM控制 ----
    // Bank A = 当前帧(读),Bank B = 下一帧(写)
    assign sram_a_addr  = word_cnt;
    assign sram_a_dout  = {SRAM_W{1'b0}};  // 不写Bank A
    assign sram_a_we    = 1'b0;             // 只读Bank A

    assign sram_b_addr  = word_cnt;
    assign sram_b_dout  = write_buf;        // 写Bank B
    assign sram_b_we    = (phase == 2'd2);  // 计算阶段写

    // ---- 主逻辑 ----
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            word_cnt     <= {ADDR_W{1'b0}};
            bit_cnt      <= 3'd0;
            phase        <= 2'd0;
            neighborhood <= 3'd0;
            write_buf    <= {SRAM_W{1'b0}};
            frame_done_r <= 1'b0;
        end else begin
            frame_done_r <= 1'b0;

            if (start || phase != 2'd0) begin
                case (phase)
                    2'd0: begin  // 读SRAM Bank A
                        phase <= 2'd1;
                    end
                    2'd1: begin  // 数据到达,处理每个bit
                        for (integer b = 0; b < SRAM_W; b = b + 1) begin
                            // 提取3位邻域
                            neighborhood = {1'b0, sram_a_din[b], 1'b0}; // 简化
                            // 应用规则
                            write_buf[b] = rule[neighborhood];
                        end
                        phase <= 2'd2;
                    end
                    2'd2: begin  // 写SRAM Bank B
                        if (word_cnt == TOTAL_WORDS - 1) begin
                            // 一帧完成,交换Bank
                            word_cnt     <= {ADDR_W{1'b0}};
                            frame_done_r <= 1'b1;
                            phase        <= 2'd0;
                        end else begin
                            word_cnt <= word_cnt + 1'd1;
                            phase    <= 2'd0;
                        end
                    end
                    default: phase <= 2'd0;
                endcase
            end
        end
    end

    assign busy       = (phase != 2'd0);
    assign frame_done = frame_done_r;

endmodule

🖥️ VGA显示控制器

一维CA的演化历史天然适合VGA显示——每行显示一步,逐行向下滚动。

CA-VGA显示控制器

// ============================================================================
// ca_vga_display.v - 一维CA的VGA显示控制器
// 每行=一步CA演化,水平方向=元胞位置
// 支持640x480 @60Hz VGA
// ============================================================================
module ca_vga_display #(
    parameter WIDTH   = 640,        // CA网格宽度=水平像素
    parameter HEIGHT  = 480,        // 显示行数=演化步数
    parameter H_SYNC  = 96,
    parameter H_BP    = 48,
    parameter H_FP    = 16,
    parameter V_SYNC  = 2,
    parameter V_BP    = 33,
    parameter V_FP    = 10
)(
    input  wire             clk,        // 25.175MHz像素时钟
    input  wire             rst_n,
    input  wire [WIDTH-1:0] ca_row,    // 当前CA行数据
    input  wire             ca_valid,   // CA数据有效
    output wire             hsync,
    output wire             vsync,
    output wire             video_en,   // 视频使能
    output wire [2:0]       rgb         // 3位颜色(1R+1G+1B)
);

    // ---- 时序计数器 ----
    reg [9:0] h_cnt;  // 水平计数器
    reg [9:0] v_cnt;  // 垂直计数器

    // 时序参数
    localparam H_TOTAL = 800;  // 640+96+48+16
    localparam V_TOTAL = 525;  // 480+2+33+10

    // ---- 时序生成 ----
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            h_cnt <= 10'd0;
            v_cnt <= 10'd0;
        end else begin
            if (h_cnt == H_TOTAL - 1) begin
                h_cnt <= 10'd0;
                if (v_cnt == V_TOTAL - 1)
                    v_cnt <= 10'd0;
                else
                    v_cnt <= v_cnt + 10'd1;
            end else begin
                h_cnt <= h_cnt + 10'd1;
            end
        end
    end

    // ---- 同步信号 ----
    assign hsync = (h_cnt >= 640+16 && h_cnt < 640+16+96) ? 1'b0 : 1'b1;
    assign vsync = (v_cnt >= 480+10 && v_cnt < 480+10+2)  ? 1'b0 : 1'b1;

    // ---- 显示区域 ----
    wire active = (h_cnt < 640 && v_cnt < 480);

    // ---- CA数据渲染 ----
    // 使用行缓冲存储当前行的CA数据
    reg [WIDTH-1:0] line_buf [0:HEIGHT-1];
    reg [9:0]       write_row;

    always @(posedge clk) begin
        if (ca_valid) begin
            line_buf[write_row] <= ca_row;
            write_row <= (write_row == HEIGHT - 1) ? 10'd0 : write_row + 10'd1;
        end
    end

    // 像素颜色
    wire pixel_on = active && line_buf[v_cnt[8:0]][h_cnt];

    assign video_en = active;
    assign rgb = pixel_on ? 3'b111 : 3'b000;  // 白色=活,黑色=死

endmodule

📊 资源使用分析

实现方式WIDTHFlip-FlopsLUTsBRAM最大频率
寄存器版本641281920~350MHz
寄存器版本1282563840~330MHz
寄存器版本2565127680~300MHz
SRAM版本1024~50~1002~250MHz
SRAM版本65536~50~10032~200MHz
💡 架构选择:寄存器版本适合小型CA(WIDTH≤256),优势是全并行、每周期一步。SRAM版本适合大型CA,但需要多个周期完成一步更新。选择取决于应用场景——实时交互用寄存器版,大规模仿真用SRAM版。

🏋️ 练习

练习5.1:综合ca_engine_1d到FPGA,测量实际资源占用和最大时钟频率。与上表对比。
练习5.2:修改SRAM版本的CA引擎,实现Bank A/B自动交换——每帧完成后,读Bank变为写Bank,写Bank变为读Bank,无需数据拷贝。
练习5.3:为CA引擎添加UART接口,支持从PC发送命令(设置规则、初始化、步进、查询状态)和接收CA状态数据。
练习5.4:实现一个"CA录制器"——将CA的每步状态存入SD卡,支持回放。估算640×480分辨率下录制1000步需要的存储空间。
练习5.5(挑战):设计流水线CA引擎——在处理第N步的写回时,同时开始第N+1步的读取。理论上可以将SRAM版本的吞吐率提高近2倍。

🏆 成就解锁

🏅 硬件引擎师

你已经完成了从理论到完整硬件引擎的跨越——寄存器版、SRAM版、VGA显示控制器。这是一维CA阶段的集大成。

🔬 从一维到二维

一维CA阶段结束!你已经掌握了CA的核心概念、规则体系、随机性、通用性和硬件实现。接下来进入二维CA——Conway生命游戏将展示更丰富的涌现行为。

📖 扩展阅读

元胞自动机课程 · 从Conway到Langton到Lattice Gas