纪念约翰·霍顿·康威,他因新冠于2020年4月11日逝世,享年82岁。

前言

Conwaylife - HDLBits

康威的生命游戏在科学美国人上刊登过,小时候还看到过,印象很深。

简单来说,就是对一个矩阵进行轮次变换,对每个元素的变换规则如下:

  • 0-1 个邻居:单元格变为 0
  • 2 个邻居:单元格状态不变
  • 3 个邻居:单元格变为 1
  • 4 个及以上邻居:单元格变为 0

要注意的是矩阵四周是“连续”的,即首尾相接。可以理解成桌面壁纸的平铺。

思路

连续的意思就是说边界处会发生“跳变”。那我整一个四周大一圈的矩阵把它包进去就可以了。

然后在每个时钟沿进行判断,再赋新的值给寄存器即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
module top_module (
input clk,
input load,
input [255:0] data,
output reg [255:0] q
);
wire [18*18-1:0] temp;
wire [ 255:0] out_temp;

// 区分对待
assign temp[17:0] = {q[240], q[255:240], q[255]};
assign temp[18*18-1:18*17] = {q[0], q[15:0], q[15]};

genvar i;
generate
for (i = 1; i < 17; i = i + 1) begin : origin_mat
assign temp[18*(i+1)-1:18*i] = {q[16*(i-1)], q[16*i-1:16*(i-1)], q[16*i-1]};
end
endgenerate

genvar j, k;
// verilog_format: off
generate
for (j = 0; j < 16; j = j + 1) begin : temp_row
for (k = 0; k < 16; k = k + 1) begin : temp_col
localparam integer idx = 18 * (j + 1) + k + 1;
// 保存最多为 8 的邻居和,4 位足矣
wire [3:0] nbr_sum;
wire loc_inst;

assign nbr_sum =
temp[idx-1] + temp[idx+1] +
temp[idx-18] + temp[idx+18] +
temp[idx-18-1]+ temp[idx-18+1] +
temp[idx+18-1]+ temp[idx+18+1];

assign loc_inst = (nbr_sum < 2) ? 1'b0 :
(nbr_sum == 2) ? temp[idx] :
(nbr_sum == 3) ? 1'b1 :
1'b0;

assign out_temp[16*j + k] = loc_inst;
end
end
endgenerate



always @(posedge clk) begin
if (load) begin
q <= data;
end
else begin
q <= out_temp;
end

end

endmodule

改进

HDLbits: verilog实现Conwaylife二维元胞自动机 - CSDN简化了数组的表示,采用二维数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//不包含testbench
module top_module(
input clk,
input load,
input [255:0] data,
output reg [255:0] q );
initial begin
q = 256'h0;
end
//填充为18*18
wire [17:0] q_t [17:0]; //填充后的q_table;
genvar i;
generate //利用循环将q_t的每一行依次填充
for(i=0;i<16;i=i+1)begin: assign_value
assign q_t[i+1][17:0] = {q[i*16], q[i*16+:16], q[i*16+15]};
end
//q_t 18*18 的第一行q_t[0]和最后一行q_t[17]单独赋值,由于这两行相当于原矩阵颠倒了后的值
assign q_t[0][17:0] = {q[15*16], q[15*16+:16], q[15*16+15]};
assign q_t[17][17:0] = {q[0], q[0*16+:16], q[0*16+15]};
endgenerate

//判断数量,
wire[3:0] q_cnt[15:0][15:0];
wire [255:0] q_temp;
genvar j,k;
generate
for (j = 0; j<16; j=j+1) begin:loop_out
for (k = 0; k<16; k=k+1) begin:loop_in
//依次计算每个位置的邻居数量q_cnt
assign q_cnt[j][k] = q_t[j+1][k] + q_t[j+1][k+2] + q_t[j][k] + q_t[j][k+2] + q_t[j][k+1] + q_t[j+2][k] + q_t[j+2][k+2] + q_t[j+2][k+1];
//利用三目运算符判断数量,决定下一时刻该位置的q值
assign q_temp[j*16+k] = (q_cnt[j][k]==4'd3)? 1'b1:
((q_cnt[j][k]==4'b0010)? q[j*16+k] :
1'b0);
end
end
endgenerate

//上升沿赋值
integer m;
always@(posedge clk)begin
if (load) begin
q<=data;
end
else begin
q<=q_temp;
end
end
endmodule