Conwaylife是FSM之前的最后一题,题解由于使用了较多的for语句,让描述少了一丝硬件逻辑描述的直观,多了一分软件算法实现的抽象。
module top_module(
input clk,
input load,
input [255:0] data,
output [255:0] q
);
// 将输入的data对应到一个“二维”寄存器组中,方便计算存活的邻居数
reg [15:0] q_2d [15:0];
// 每个cell有8个邻居,邻居存活数为0-8,共需4位二进制表示
reg [3:0] ngbr_num [255:0];
// 表示当前cell上下左右邻居的坐标、用于for循环的计数变量
integer idx_i_u, idx_i_d, idx_j_l, idx_j_r, i, j;
// 组合逻辑,统计每个cell的存活邻居数量并写入对应的4位ngbr_num寄存器组中
always @(*)
for(i = 0; i < 16; i = i + 1)
for(j = 0; j < 16; j = j + 1) begin
idx_i_u = (i == 0) ? i - 1 + 16 : i - 1;
idx_i_d = (i == 15) ? i + 1 - 16 : i + 1;
idx_j_l = (j == 0) ? j - 1 + 16 : j - 1;
idx_j_r = (j == 15) ? j + 1 - 16 : j + 1;
ngbr_num[16*i + j]= q_2d[idx_i_u][idx_j_l] + q_2d[idx_i_u][j] + q_2d[idx_i_u][idx_j_r] +
q_2d[i ][idx_j_l] + + q_2d[i ][idx_j_r] +
q_2d[idx_i_d][idx_j_l] + q_2d[idx_i_d][j] + q_2d[idx_i_d][idx_j_r];
end
// 时序逻辑,load高电平则将data一一对应写入q_2d中,用于初始化,
// 不初始化时则根据每个cell的存活邻居数量决定该cell的下一个状态值
always @(posedge clk)
if(load)
for(i = 0; i < 16; i = i + 1)
for(j = 0; j < 16; j = j + 1)
q_2d[i][j] <= data[16*i + j];
else begin
for(i = 0; i < 16; i = i + 1)
for(j = 0; j < 16; j = j + 1)
case(ngbr_num[16*i + j])
4'd2 : q_2d[i][j] <= q_2d[i][j];
4'd3 : q_2d[i][j] <= 1'b1;
default : q_2d[i][j] <= 1'b0;
endcase
end
// 组合逻辑,类似上述初始化的描述方法,将各个cell的值一一对应输出
always @(*)
for(i = 0; i < 16; i = i + 1)
for(j = 0; j < 16; j = j + 1)
q[16*i + j] = q_2d[i][j];
endmodule
代码中三个always块,只有在load时(根据题目描述)以及由cell的现态得到次态时才需要时序逻辑实现,其余无论是计算cell的存活邻居数还是对cell状态进行输出都是组合逻辑。本题还有许多更加简便的解法,但是这种方法较为直观,更便于理解Conwaylife,故记录于此。