90 号一维元胞自动机【FPGA 常用数字设计课题】
规则 90 是一种具备诸多有趣特性的一维元胞自动机。其演化规则十分简单:存在一排一维元胞阵列,每个元胞只有开启、关闭两种状态。每一次迭代时刻,任意元胞的下一状态等于它左右两个相邻元胞当前状态的异或值。
下面用更详尽的表格形式描述该规则:单个元胞的下一状态由它自身以及左右两侧邻居共同决定。

("规则 90" 这个名称来源于次态列的二进制数值:01011010 转换成十进制正好是 90。)
请设计该数字电路:搭建一套包含 512 个元胞的系统,状态寄存器为q511:0;每个时钟周期完成一次迭代(前进一个时间步)。 输入信号load有效时,需将data511:0的数值载入元胞系统,作为系统初始状态。 边界处理规则:虚拟边界元胞q-1(数组最左侧外)与q512(数组最右侧外)的状态均固定为 0(关闭状态)
参考答案
java
/*
=====================================================================
模块功能:512位Rule90一维元胞自动机,支持并行载入初始数据
核心算法Rule90:单个细胞下一状态 = 左侧邻居值 XOR 右侧邻居值
控制逻辑:load=1并行加载外部data;load=0按Rule90迭代更新细胞阵列
边界处理:最左细胞无左邻居、最右细胞无右邻居,缺失邻居默认逻辑0
位序约定:q[511]最右端细胞、q[0]最左端细胞,整段共512个连续元胞
=====================================================================
*/
module top_module(
input clk, // 全局同步时钟,上升沿执行加载/迭代状态刷新
input load, // 并行加载使能(高电平有效):1载入初始值;0执行Rule90演化
input [511:0] data, // 512位并行预置数据,load有效时写入寄存器作为初始状态
output reg [511:0] q // 512位元胞状态寄存器,保存每一拍演化结果
);
always @(posedge clk) begin
if (load) begin
// 并行加载模式:时钟上升沿直接把外部data整体写入q,重置元胞初始状态
q <= data;
end
else begin
// ========== Rule90 核心迭代公式拆解 ==========
// 规则定义:q[i]次态 = 左邻居q[i-1] XOR 右邻居q[i+1]
// 1.{1'b0, q[511:1]}:整体右移1位、最高位补0 → 每一位对齐自身【右邻居】
// 例:原q[i+1]移到当前i位置,最右端q[511]右邻居空缺补0
// 2.{q[510:0], 1'b0}:整体左移1位、最低位补0 → 每一位对齐自身【左邻居】
// 例:原q[i-1]移到当前i位置,最左端q[0]左邻居空缺补0
// 3.两组向量逐位异或,一次性算出全部512个元胞下一时刻状态
q <= {1'b0, q[511:1]} ^ {q[510:0], 1'b0};
end
end
endmodule
输出结果

110 号一维元胞自动机
规则110是一种具备诸多有趣特性(例如具备图灵完备性)的一维元胞自动机。系统包含一维元胞阵列(元胞状态分为开启或关闭两种)。在每一个时间步长中,每个元胞的状态都会发生改变。在规则110中,每个元胞的下一状态仅由自身及其左右两个相邻元胞决定,具体遵循下表所示规则:

"规则110"这一名称来源于状态转移列的二进制数值:二进制数01101110转换为十进制即为110。在本电路中,搭建一个包含512个元胞的系统(信号q511:0),每个时钟周期完成一次单步状态演化。加载输入信号有效时,系统会将data511:0的数据载入作为初始状态。规定边界元胞q-1与q512的状态均为0(关闭状态)。
逻辑表达式为:nexti=(C⊕R)∣(L‾⋅C)next_i = (C \oplus R) | (\overline{L} \cdot C)nexti=(C⊕R)∣(L⋅C)
参考答案
java
/*
=====================================================================
模块功能:512位Rule110一维元胞自动机,支持并行载入初始状态
经典Rule110规则:单格次态 = (本位 ^ 左邻) | ( ~右邻 & 本位 )
控制逻辑:load=1 全局并行加载外部data初始化阵列;load=0 按Rule110逐时钟迭代演化
边界约定:最左端元胞无左邻居、最右端元胞无右邻居,空缺邻居默认逻辑0
位序:q[511]最右端元胞、q[0]最左端元胞,总长度512bit
应用背景:Rule110是已被证明具备图灵完备性的一维元胞自动机,可实现通用计算
=====================================================================
*/
module top_module(
input clk, // 系统同步时钟,仅上升沿触发元胞阵列整体状态刷新
input load, // 并行加载使能(高电平有效):1载入初始数据;0执行Rule110演化迭代
input [511:0] data, // 512bit外部预置数据,load拉高时写入寄存器作为元胞初始状态
output reg [511:0] q // 512位元胞状态寄存器,保存当前迭代周期所有细胞电平
);
always @(posedge clk) begin
if (load) begin
// 并行加载分支:时钟上升沿直接覆盖q的值,重置整列元胞初始分布
q <= data;
end else begin
// ===================== Rule110核心迭代公式分步拆解 =====================
// 基础符号定义(对任意中间位q[i]):
// 本位:q[i];左邻居:L=q[i-1];右邻居:R=q[i+1]
// 标准Rule110布尔式:q_next = (q[i]^L) | (~R & q[i])
// 1.左邻居向量 {q[510:0],1'b0}:全体左移1位、最低位补0,q[i]位置对齐左邻q[i-1],q[0]左邻空缺补0
// 2.右邻居向量 {1'b0,q[511:1]}:全体右移1位、最高位补0,q[i]位置对齐右邻q[i+1],q[511]右邻空缺补0
// 运算优先级:取反~ > 与& > 异或^ > 或|,和代码书写顺序匹配
q <= (q ^ {q[510:0], 1'b0}) | (~{1'b0, q[511:1]} & q);
end
end
endmodule
输出结果

康威生命游戏(16×16 二维网格)
康威生命游戏是一种二维元胞自动机。
该"游戏"运行在二维元胞网格上,每个元胞状态为1(存活)或0(死亡)。每个时间步长,所有元胞会根据自身周围存活邻居的总数切换状态,规则如下:
- 存活邻居数量为0~1个:元胞变为死亡(0);
- 存活邻居数量为2个:元胞状态保持不变;
- 存活邻居数量为3个:元胞变为存活(1);
- 存活邻居数量≥4个:元胞变为死亡(0)。
该游戏理论定义在无限网格上。本电路采用16×16有限网格,且网格为环面结构(边界首尾循环相连)。举例:角落元胞坐标(0,0)拥有8个相邻元胞,坐标分别为(15,1)、(15,0)、(15,15)、(0,1)、(0,15)、(1,1)、(1,0)、(1,15)。
16×16网格由长度256的一维向量表示:每一行16个元胞对应一段子向量,其中q15:0代表第0行,q31:16代表第1行,以此类推。(本工具支持SystemVerilog语法,你也可以直接使用二维向量。)
端口说明:
- load:时钟上升沿将数据载入状态寄存器q,用于加载游戏初始布局;
- q:存储16×16网格当前游戏状态,每个时钟周期更新一次。
游戏状态需要在每个时钟周期向前推演一个时间步。
补充背景:生命游戏发明者、数学家约翰·康威于2020年4月11日因新冠逝世。

参考答案
java
/*
=====================================================================
模块功能:16×16康威生命游戏(环形环绕边界),支持并行加载初始状态+逐时钟迭代演化
网格存储:256bit一维向量映射二维网格,q[15:0]对应第0行、每行16个细胞,总行列范围r∈[0,15],c∈[0,15]
边界规则:环面(Torus)边界,首尾行、首尾列循环相邻,无边缘截断空缺邻居
演化规则:标准康威生命游戏4条核心判定规则
工作模式:load=1并行写入初始data;load=0时钟上升沿刷新下一代细胞状态
架构拆分:组合逻辑预计算全网格次态next_q + 时序逻辑同步刷新寄存器q,三段式经典写法
=====================================================================
*/
module top_module(
input clk, // 系统同步时钟,上升沿统一刷新整张网格的细胞状态
input load, // 并行加载使能(高电平有效):时钟沿载入初始网格数据,重置游戏状态
input [255:0] data, // 256bit一维初始向量,映射16×16二维网格,load拉高时写入寄存器
output reg [255:0] q // 细胞状态主寄存器,保存当前完整网格所有细胞存活/死亡状态
);
reg [255:0] next_q; // 组合逻辑缓存:预计算得到下一时刻整张网格的细胞状态
//====================组合逻辑块:遍历全网格、按生命游戏规则计算次态====================
always @(*) begin
// Verilog规范:循环计数、临时整型变量统一在always块开头声明
integer idx; // 一维向量全局遍历下标,取值0~255,逐个访问每个细胞
integer r, c; // 当前细胞对应的二维行号r(0~15)、列号c(0~15)
integer ru, rd, cl, cr; // 环绕修正后的上一行、下一行、左一列、右一列编号
integer n0,n1,n2,n3,n4,n5,n6,n7; // 8邻域邻居对应的一维向量下标
integer sum; // 统计8个邻居里存活(电平=1)的细胞总数量
reg curr; // 寄存当前中心细胞自身的存活状态(1存活/0死亡)
next_q = 256'd0; // 初始化下一代网格缓存全0,避免未赋值位出现不定态
// 双层逻辑等价单层循环:遍历全部256个网格细胞
for(idx = 0; idx < 256; idx = idx + 1) begin
// 一维下标转二维行列:每行固定16列,整除取行、取余取列
r = idx / 16;
c = idx % 16;
// 环面边界环绕运算:首尾行列循环衔接,解决边缘细胞邻居缺失问题
ru = (r - 1 + 16) % 16; // 上一行:首行r=0向上跳转至末行15,+16规避负数取余异常
rd = (r + 1) % 16; // 下一行:末行r=15向下跳转至首行0
cl = (c - 1 + 16) % 16; // 左一列:首列c=0向左跳转至末列15
cr = (c + 1) % 16; // 右一列:末列c=15向右跳转至首列0
// 计算8个相邻细胞一维下标,完整覆盖摩尔8邻域
n0 = ru * 16 + cl; // 左上邻居
n1 = ru * 16 + c; // 正上邻居
n2 = ru * 16 + cr; // 右上邻居
n3 = r * 16 + cl; // 左侧邻居
n4 = r * 16 + cr; // 右侧邻居
n5 = rd * 16 + cl; // 左下邻居
n6 = rd * 16 + c; // 正下邻居
n7 = rd * 16 + cr; // 右下邻居
// 累加邻居存活总数:高电平代表细胞存活,直接数值求和
sum = q[n0] + q[n1] + q[n2] + q[n3] + q[n4] + q[n5] + q[n6] + q[n7];
curr = q[idx]; // 读取当前中心细胞本身状态
//============康威生命游戏标准演化规则============
if(sum == 3) begin
// 规则1:任意位置有恰好3个存活邻居,细胞复活(无论原本生死)
next_q[idx] = 1'b1;
end
else if(sum == 2) begin
// 规则2:恰好2个存活邻居,细胞维持原有状态不变
next_q[idx] = curr;
end
else begin
// 规则3:邻居≤2(饥饿消亡)或≥4(拥挤消亡),细胞直接死亡
next_q[idx] = 1'b0;
end
end
end
//====================时序逻辑块:时钟同步执行加载/状态迭代====================
always @(posedge clk) begin
if(load) begin
// 加载模式:时钟上升沿用外部data覆盖全局状态,初始化/重置网格
q <= data;
end
else begin
// 演化模式:载入组合逻辑提前算好的下一代网格状态,推进一个时间步
q <= next_q;
end
end
endmodule
输出结果
