对于序列检测题目,常规的解法有两种:状态机法和序列缓存对比法。
状态机法的过程类似于:在初始状态中,先判断第一位是否符合,若符合则进入下一个状态,判断第二位是否符合;若第一位不符合则保持在初始状态,直到第一位匹配。如前两位匹配,则判断第三位是否符合,若第一位匹配,最新输入的数值和目标序列的第二位不匹配,则根据最新一位是否匹配第一位,进入第一位匹配状态或者初始状态。依次类推。
序列缓存对比法,则是将八个时刻的数据缓存,作为一个数组,每个时刻的输入位于数组的末尾,数组其它元素左移,把最早输入的数据移出。然后将数组和目标序列对比,如果数组和目标序列相等,则说明出现目标序列。
序列缓存对比法在实现上比较简单。首先声明一个数组,缓存八个时刻的a输入的数值。移位可以通过位截取操作和位拼接操作实现:a_tem[6:0]表示截取a_tem的低7位,{a_tem[6:0],a}表示把a_tem[6:0]和新输入的数值a拼接,a位于低位。
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input a,
output reg match
);
reg [7:0] a_tem;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
match <=1'b0;
end
else if (a_tem == 8'b0111_0001) begin
match <= 1'b1;
end
else begin
match <= 1'b0;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
a_tem <= 8'b0;
end
else begin
a_tem <= {a_tem[6:0],a};
end
end
endmodule
序列缓存对比法,则是将九个时刻的数据缓存,作为一个数组,每个时刻的输入位于数组的末尾,数组其它元素左移,把最早输入的数据移出。然后截取数组的前三位和目标序列011对比,截取数组的后三位和目标序列110对比,如果两段数组都和目标序列相等,则说明出现目标序列。
序列缓存对比法在实现上比较简单,本题采用该方法实现。首先声明一个数组,缓存九个时刻的a输入的数值。移位可以通过位截取操作和位拼接操作实现:a_tem[7:0]表示截取a_tem的低7位,{a_tem[7:0],a}表示把a_tem[7:0]和新输入的数值a拼接,a位于低位。
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input a,
output match
);
reg [8:0] a_tem;
reg match_f;
reg match_b;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
match_f <=1'b0;
end
else if (a_tem[8:6] == 3'b011) begin
match_f <= 1'b1;
end
else begin
match_f <= 1'b0;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
match_b <= 1'b0;
end
else if (a_tem[2:0] == 3'b110) begin
match_b <= 1'b1;
end
else begin
match_b <= 1'b0;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
a_tem <= 9'b0;
end
else begin
a_tem <= {a_tem[7:0],a};
end
end
assign match = match_b && match_f;
endmodule
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input a,
output reg match
);
reg [8:0] sequence;
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
sequence <= 9'b0;
end
else begin
sequence <= {sequence[7:0],a};
end
end
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
match <= 0;
end
else if (sequence[8:6] == 3'b011 && sequence[2:0] == 3'b110) begin
match <= 1;
end
else begin
match <= 0;
end
end
endmodule
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input a,
output reg match
);
reg [8:0] val;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
val <= 9'b0;
end else begin
val <= {val[7:0],a};
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
match <= 1'b0;
end else begin
casex (val)
9'b011xxx110 : match <= 1'b1;
default : match <= 1'b0;
endcase
end
end
endmodule
使用数选器选择出来对应的位,再做同或与最后做与运算,资源用的也很少。
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input data,
output reg match,
output reg not_match
);
reg [2:0] cnt;
reg cmp;
reg detect_cmp;
parameter detect = 6'b011100;
always@(posedge clk or negedge rst_n)begin
if(! rst_n)
cnt <= 3'd0;
else if(cnt == 3'd5)
cnt <= 3'd0;
else
cnt <= cnt + 3'd1;
end
always@(*)begin
case(cnt)
3'd0: cmp = 1'd0;
3'd1: cmp = 1'd1;
3'd2: cmp = 1'd1;
3'd3: cmp = 1'd1;
3'd4: cmp = 1'd0;
3'd5: cmp = 1'd0;
default: cmp = 1'd0;
endcase
end
always@(posedge clk or negedge rst_n) begin
if(! rst_n)
detect_cmp <= 1'd1;
else if(cnt == 3'd5)
detect_cmp <= 1'd1;
else
detect_cmp <= detect_cmp && (~( cmp^ data));
end
always@(posedge clk or negedge rst_n) begin
if(! rst_n)
match <= 1'd0;
else if((detect_cmp )&&(cnt == 3'd5))
match <= 1'd1;
else
match <= 1'd0;
end
always@(posedge clk or negedge rst_n) begin
if(! rst_n)
not_match <= 1'd0;
else if((!detect_cmp)&&(cnt == 3'd5))
not_match <= 1'd1;
else
not_match <= 1'd0;
end
endmodule
首先确定second的取值逻辑:当minute=60时停止计数,即保持second为0;当second=60时,下一个周期second置为1。其余情况second 等于前一时刻的值加一。 然后明确minute的取值逻辑:当second=60,minute等于前一时刻的值加一。其余情况,minute保持不变。
`timescale 1ns/1ns
module count_module(
input clk,
input rst_n,
output reg [5:0]second,
output reg [5:0]minute
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
minute <= 6'd0;
end
else if (second == 6'd60) begin
minute <= minute + 1;
end
else begin
minute <= minute;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
second <= 6'd0;
end
else if (second == 6'd60) begin
second <= 6'd1;
end
else if (minute == 60) begin
second <=0;
end
else
second <= second + 1'd1;
end
endmodule
首先明确number的取值逻辑,声明number变量为4位无符号数,数值每个时钟加一,则每次数值达到15,下一个时钟因为位宽的限制,自动变为1,可以实现十六进制计数。当set信号为1时,将set_num的值赋给number。
然后确定zero的取值逻辑,在默认情况下为0,当number=0时,zero值为1。
因为判断number4’d0需要一个时钟,zero信号为1,总是滞后number0一个时钟周期。所以可以考虑将number延迟一个时钟再输出。使用num变量代替上述的number,再通过以下语句实现number延迟一个时钟输出。【因为number是寄存器类型,无法通过组合逻辑进行阻塞赋值同时匹配。 如果直接使用number进行zero的判断来说,zero肯定是比number过“0”时刻慢一拍的; 所以不妨使用num_reg,进行延迟; 因为zero肯定是比num_reg慢一拍的,所以再通过num_reg延迟一拍给number,则number与zero同步输出匹配;】
`timescale 1ns/1ns
module count_module(
input clk,
input rst_n,
input set,
input [3:0] set_num,
output reg [3:0]number,
output reg zero
);
reg [3:0] num;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
zero = 1'd0;
end
else if (num == 4'd0) begin
zero <= 1'b1;
end
else begin
zero <= 1'b0;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
num <= 4'b0;
end
else if (set) begin
num <= set_num;
end
else begin
num <= num + 1'd1;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
number <= 1'd0;
end
else begin
number <= num;
end
end
endmodule
首先确定zero的取值逻辑,在默认情况下为0,当number=0时,zero值为1。 always @(posedge clk or negedge rst_n)
然后将mode的值作为if-else的判断条件,当mode为1时,number每个时钟周期加一,当mode为0时,number每个时钟周期减一。
按照以上代码,因为判断number4’d0需要一个时钟,zero信号为1,总是滞后number0一个时钟周期。所以可以考虑将number延迟一个时钟再输出。使用num变量代替上述的number,再通过以下语句实现number延迟一个时钟输出。
`timescale 1ns/1ns
module count_module(
input clk,
input rst_n,
input mode,
output reg [3:0]number,
output reg zero
);
reg [3:0] num;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
zero <= 1'd0;
end
else if (num == 4'd0) begin
zero <= 1'b1;
end
else begin
zero <= 1'b0;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
num <= 4'b0;
end
else if (mode) begin
if (num == 9)
num <= 0;
else
num <= num + 1'd1;
end
else if (!mode) begin
if (num == 0) num <= 9;
else num <= num - 1'd1;
end
else
num <= num ;
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
number <= 4'd0;
end
else begin
number <= num;
end
end
endmodule
要实现RAM,首先要声明数据的存储空间,例如:[3:0] rom [7:0];变量名称ram之前的[3:0]表示每个数据具有多少位,指位宽;变量名称ram之后的[7:0]表示需要多少个数据,指深度,注意这里深度为8,应该是使用[7:0],而不是[2:0];
声明存储变量之后,需要对ram进行初始化,写入数据,当write_en有效,向write_addr写入write_data,当read_en有效,根据输入的read_addr输出read_data。需要注意的是,题目要求实现真双端口RAM,即可以同时写入和读出,所以需要使用两个always语句块实现写入和读出逻辑,不可以在同一个always块中使用if-else if-else if结果。
`timescale 1ns/1ns
module ram_mod(
input clk,
input rst_n,
input write_en,
input [7:0]write_addr,
input [3:0]write_data,
input read_en,
input [7:0]read_addr,
output reg [3:0]read_data
);
reg [3:0] myRAM [7:0];
reg [8:0] i;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (i = 0;i < 256;i = i+1)
myRAM[i] = 0;
end
else begin
myRAM[write_addr] <= write_en ? write_data:myRAM[write_addr];
end
end
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
read_data <= 0;
end
else
read_data <= read_en?myRAM[read_addr]:read_data;
end
endmodule