Rule 90
.数组的异或规则是对应分量两两异或,得到一个新的数组。
Solution
module top_module(
input clk,
input load,
input [511:0] data,
output [511:0] q );
always @(posedge clk) begin
if (load) //别忘了先加载
q <= data;
else
q <= {1'b0,q[511:1]} ^ {q[510:0],1'b0};
end
endmodule
Rule 110
将左、中、右状态组合为 3 位索引,查表(Rule 110 的转移表)得到下一状态。
将左、中、右分别作为三个变量(含有512个分量的数组),作卡诺图:
L\C R | 00 | 01 | 11 | 10 |
---|---|---|---|---|
0 | 0 | 1 | 1 | 1 |
1 | 0 | 1 | 0 | 1 |
于是,$C^{(n+1)} = \bar{C}R +\bar{L}C+C\bar{R}$
Solution
module top_module(
input clk,
input load,
input [511:0] data,
output [511:0] q
);
wire [511:0] L,C,R;
assign L = {1'b0,q[511:1]};
assign C = q;
assign R = {q[510:0],1'b0};
always @(posedge clk) begin
if(load)
q <= data;
else
q <= (~C & R) | (~L & C) | (C & ~R);
end
endmodule
Conway's Game of Life 16x16
题意
生命游戏是二维元胞自动机,每个细胞(cell
)状态为1(活)或0(死),下一状态由8 个邻居的存活数决定:
邻居存活数 | 细胞下一状态 |
---|---|
0-1 个 | 死亡(变 0) |
2 个 | 保持当前状态 |
3 个 | 复活(变 1,即使之前是死的) |
≥4 个 | 死亡(变 0) |
索引转换
由一维索引转换到二维索引:
行号
i
:- 用
idx
除以16
取整(舍去余数); - 等效于:
idx >> 4
(右移 4 位 = 除以 2⁴)。
- 用
列号
j
:j
=idx % 16
(取模运算,即余数);- 等效于按位与:
idx & 15
(15 的二进制是1111
,按位与之后达到保留低 4 位(即余数)的效果)。
由二维索引转换到一维索引: (i, j)
的一维索引为 i * 16 + j
.
右移位运算符
>>
是 右移位运算符:
- 逻辑右移(
>>
)
将二进制数向右移动,左侧空出的位用 0 填充,不考虑符号位。
语法:A >> N
(将 A 右移 N 位)。 - 算术右移(
>>>
)
将二进制数向右移动,左侧空出的位用符号位填充(即保持负数的符号)。
语法:A >>> N
(将 A 右移 N 位)。
Solution
module top_module(
input clk,
input load,
input [255:0] data,
output [255:0] q
);
reg [255:0] q_reg; // 模块级寄存器存储当前状态
assign q = q_reg; // 输出直接映射到寄存器
always @(posedge clk) begin
// ------- 步骤1:声明过程块内的变量(必须放在块的开头)---------
reg [255:0] next_q; //存储下一状态(局部reg)
integer idx; //当前细胞的一维索引
integer dr; //相对于当前细胞的行偏移(Delta Row)
integer dc; //相对于当前细胞的列偏移(Delta Column)
integer i; //当前细胞的二维行索引
integer j; //当前细胞的二维列索引
integer r; //邻居的绝对坐标的行索引
integer c; //邻居的绝对坐标的列索引
integer neighbor_idx; //邻居的一维索引
integer cnt; //邻居的存活数目
if (load) begin
// 加载模式:直接赋值
q_reg <= data;
end else begin
// --------- 步骤2:遍历每个细胞,计算下一状态 ------------
for (idx = 0; idx < 256; idx = idx + 1) begin
i = idx >> 4; // 计算当前细胞的行号(0~15)
j = idx & 15; // 计算当前细胞的列号(0~15)
cnt = 0; // 邻居存活数初始化
// 遍历8个邻居(dr, dc ∈ {-1, 0, 1},左右位移只能取1或-1,所以待会需要跳过自身)
for (dr = -1; dr <= 1; dr = dr + 1) begin
for (dc = -1; dc <= 1; dc = dc + 1) begin
if (dr == 0 && dc == 0) continue; // 跳过当前细胞
// 处理环面边界(确保行/列在0~15范围内)
r = (i + dr + 16) % 16; // +16避免负数,再取模
c = (j + dc + 16) % 16;
neighbor_idx = r * 16 + c; // 邻居的一维索引
// 统计存活邻居
if (q_reg[neighbor_idx]) begin
cnt = cnt + 1;
end
end
end
// --------- 步骤3:根据规则更新下一状态 -------------
if (cnt < 2 || cnt >= 4) begin
next_q[idx] = 1'b0;
end else if (cnt == 2) begin
next_q[idx] = q_reg[idx]; // 保持当前状态
end else begin // cnt == 3
next_q[idx] = 1'b1; // 复活
end
end
// 统一更新状态(时序安全,避免竞争)
q_reg <= next_q;
end
end
endmodule
本题是硬件实现元胞自动机的经典方法!