流水线 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 │ │ │ └──────────────────────────────────────────┘ │ └──────────────────────────────────────────────────┘
对于小网格(WIDTH ≤ 256),可以直接用寄存器存储状态,所有元胞并行计算。
// ============================================================================
// 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
对于大网格(WIDTH = 1024~65536),需要使用外部SRAM。设计要点是时分复用——每周期从SRAM读取3个邻域值,计算下一状态,写回。
// ============================================================================
// 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
一维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
| 实现方式 | WIDTH | Flip-Flops | LUTs | BRAM | 最大频率 |
|---|---|---|---|---|---|
| 寄存器版本 | 64 | 128 | 192 | 0 | ~350MHz |
| 寄存器版本 | 128 | 256 | 384 | 0 | ~330MHz |
| 寄存器版本 | 256 | 512 | 768 | 0 | ~300MHz |
| SRAM版本 | 1024 | ~50 | ~100 | 2 | ~250MHz |
| SRAM版本 | 65536 | ~50 | ~100 | 32 | ~200MHz |
ca_engine_1d到FPGA,测量实际资源占用和最大时钟频率。与上表对比。
你已经完成了从理论到完整硬件引擎的跨越——寄存器版、SRAM版、VGA显示控制器。这是一维CA阶段的集大成。
一维CA阶段结束!你已经掌握了CA的核心概念、规则体系、随机性、通用性和硬件实现。接下来进入二维CA——Conway生命游戏将展示更丰富的涌现行为。
元胞自动机课程 · 从Conway到Langton到Lattice Gas