Edge Detector Positive
Edge Detector Positive 동작은 아래의 그림으로 설명된다.
Clock이 인가되며, cp의 값이 1이 되는 동안 Clock가 posedge일 때 Current는 1이 된다.
Current가 1인 동안 Clock이 posedge 되는 순간 old는 1로 변경된다.
Positive Edge detector의 코드는 아래와 같다.
module edge_detector_p(
input clk, reset_p,
input cp,
output p_edge, n_edge);
reg ff_cur, ff_old; //flip flop current
always @(posedge clk or posedge reset_p)begin
if(reset_p)begin
ff_cur <= 0;
ff_old <= 0;
end
else begin
ff_cur <= cp; //non blocking "<="
ff_old <= ff_cur;
end
end
assign p_edge = ({ff_cur, ff_old} == 2'b10) ? 1 : 0;
assign n_edge = ({ff_cur, ff_old} == 2'b01) ? 1 : 0;
endmodule
코드를 시뮬레이션하면 아래와 같은 파형이 출력된다.
Edge Detector Negative
Negative Edge Detector는 앞서 나온 Positive Edge Detector와는 달리, Clock이 0이 되는 Falling Edge인 순간에 파형 변화가 이루어진다.
module edge_detector_n(
input clk, reset_p,
input cp,
output p_edge, n_edge);
reg ff_cur, ff_old; //flip flop current
always @(negedge clk or posedge reset_p)begin
if(reset_p)begin
ff_cur <= 0;
ff_old <= 0;
end
else begin
ff_cur <= cp; //non blocking "<="
ff_old <= ff_cur;
end
end
assign p_edge = ({ff_cur, ff_old} == 2'b10) ? 1 : 0;
assign n_edge = ({ff_cur, ff_old} == 2'b01) ? 1 : 0;
endmodule
이번에 다룰 레지스터는 총 4가지로, SISO, SIPO, PISO, PIPO로 구분된다.
Shift register SISO
직렬입력 직렬출력 레지스터를 구현하려고 한다.
SISO n비트 시프트 레지스터 코드는 아래와 같다.
module shift_register_SISO_NBIT_n #(parameter N = 8)(
input clk, reset_p,
input d,
output q);
reg [N-1:0] siso_reg;
always @(negedge clk or posedge reset_p)begin
if(reset_p)siso_reg <= 0;
else begin
siso_reg <= {d, siso_reg[N-1:1]};
end
end
assign q = siso_reg[0];
endmodule
시뮬레이션 소스로(Test Bench 로) 시프트 레지스터를 작성하려면 다음과 같은 과정을 필요로 한다.
negative edge에서 바뀌는 코드는 아래와 같다.
Test bench 파일은 입출력이 없다. (= 첫 번째 줄 코드에 아무것도 기재하지 않는다.)
입력은 reg로, 출력은 wire로 선언하며, initial begin에는 초기값을 부여한다.
두 번째 initial 문에는 시뮬레이션할 내용들을 적어주면 된다.
그리고 시작하는 곳에 DUT(Design Under Test)를 선언하여 코드를 진행하면 된다.
구현한 코드는 아래와 같다.
module tb_shift_register_SISO_Nbit_n();
reg clk, reset_p; //input == reg
reg d; // input == reg
wire q; // output == wire
parameter data = 4'b1010;
shift_register_SISO_NBIT_n #(.N(4)) DUT( //Design Under Test
.clk(clk), .reset_p(reset_p),
.d(d),
.q(q));
initial begin
clk = 0;
reset_p = 1;
d = data[0];
end
always #5 clk = ~clk; // #5 = 5ns(Time scale) Time delay, #5 clk = ~clk => 10ns clock
integer i;
initial begin
#10; // 10ns Clock Start
reset_p = 0;
for(i = 0; i < 4; i=i+1)begin
d = data[i]; #10;
end
#40;
$finish; // Finist Simulation
end
endmodule
출력 파형을 살펴보면, D값이 1이 될 때마다 data값에 1을 주면서, 왼쪽에서 오른쪽으로 data값은 시프트 되는 것을 확인할 수 있다.
이번에는 4bit가 아닌 N을 8bit로 선언하고, '11010011'로 선언하면 아래와 같은 파형이 출력된다.
module tb_shift_register_SISO_Nbit_n();
reg clk, reset_p; //input == reg
reg d; // input == reg
wire q; // output == wire
parameter data = 8'b11010011;
shift_register_SISO_NBIT_n #(.N(8)) DUT( //Design Under Test
.clk(clk), .reset_p(reset_p),
.d(d),
.q(q));
initial begin
clk = 0;
reset_p = 1;
d = data[0];
end
always #5 clk = ~clk; // #5 = 5ns(Time scale) Time delay, #5 clk = ~clk => 10ns clock
integer i;
initial begin
#10; // 10ns Clock Start
reset_p = 0;
for(i = 0; i < 8; i=i+1)begin
d = data[i]; #10;
end
#40;
$finish; // Finist Simulation
end
endmodule
Shift register SIPO
이번에는 직렬입력 병렬출력 레지스터를 구현하고자 한다.
SISO 때와 마찬가지로, SIPO_Nbit_n을 먼저 구현한 후, DUT로 선언하여 Test bench를 구현한다.
코드는 아래와 같다.
module tb_shift_register_SIPO_Nbit_n();
reg clk, reset_p;
reg d;
reg rd_en; //read enable, 3 state buffer
wire [7:0] q;
parameter data = 8'b10100011;
shift_register_SIPO_Nbit_n #(.N(8)) DUT(clk, reset_p, d, rd_en, q);
initial begin
clk = 0;
reset_p = 1;
d = data[0];
rd_en = 0;
end
always #5 clk = ~clk;
integer i;
initial begin
#10;
reset_p = 0; rd_en = 1; #10;
for(i = 0; i < 8; i = i + 1)begin
d = data[i]; #10;
end
rd_en = 0; #1;
$finish;
end
endmodule
Shift register PISO
이번 동작을 구현할 것은 PISO이다. 병렬입력 직렬출력 레지스터로, 동작구조는 아래와 같다.
PISO를 구현할 코드는 아래와 같다.
module tb_shift_register_PISO_Nbit_n();
reg clk, reset_p;
reg [7:0] d;
reg shift_load;
wire q;
parameter data = 8'b11001010;
shift_register_PISO_Nbit_n #(.N(8)) DUT(
.clk(clk),
.reset_p(reset_p),
.d(d),
.shift_load(shift_load), // 1 = shift, 0 = load
.q(q)
);
initial begin
clk = 0;
reset_p = 1;
d = data;
shift_load = 0;
end
always #5 clk = ~clk;
initial begin
#10;
reset_p = 0;
shift_load = 0;
d = data;
#10;
shift_load = 1;
#70;
$finish;
end
endmodule
Shift register PIPO
위 그림을 보면 AND 게이트가 붙어 있는 것을 확인할 수 있다. WR 값이 0인 경우에는 입력을 받지 못하는 상황이므로, 코드를 구현할 때에는 MUX를 활용하여 구현할 예정이다.
우선 DUT를 활용하기 위하여 Nbit register 코드를 먼저 구현한다.
module register_Nbit_n #(parameter N = 8)(
input [N-1:0] in_data,
input clk, reset_p, wr_en, rd_en, // write/read enable
output [N-1:0] out_data);
reg [N-1:0] register;
always @(negedge clk or posedge reset_p)begin
if(reset_p) register = 0;
else if(wr_en) register = in_data;
end
assign out_data = rd_en ? register : 'bz; // if rd_en == 0 => Impedence
endmodule
Nbit register 코드가 구현되었다면, Test Bench를 사용하기 위해 Test Bench 코드를 작성한다.
module tb_register_Nbit_n();
parameter TEST_DATA_0 = 46;
parameter TEST_DATA_1 = 8'b00110101;
parameter TEST_DATA_2 = 8'hf7;
parameter TEST_DATA_3 = 7;
reg clk, reset_p, wr_en, rd_en;
reg [7:0] in_data;
wire [7:0] out_data;
register_Nbit_n #(.N(8)) DUT(in_data, clk, reset_p, wr_en, rd_en, out_data);
initial begin
in_data = 0;
clk = 0;
reset_p = 1;
wr_en = 0;
rd_en = 0;
end
always #5 clk = ~clk;
initial begin
#10;
reset_p = 0; #10;
in_data = TEST_DATA_0; #10;
wr_en = 1; #10;
wr_en = 0; rd_en = 1; #10;
rd_en = 0; in_data = TEST_DATA_1; #10;
wr_en = 1; #10;
wr_en = 0; rd_en = 1; #10;
rd_en = 0; in_data = TEST_DATA_2; #10;
wr_en = 1; #10;
wr_en = 0; rd_en = 1; #10;
rd_en = 0; in_data = TEST_DATA_3; #10;
wr_en = 1; #10;
wr_en = 0; rd_en = 1; #10;
$finish;
end
endmodule
출력 파형을 살펴보면, wr_en(write enable)이 1이 될 때마다 register의 값은 변경이 되며, 출력은 임피던스로 된다.
rd_en(read enable)이 1이 되는 순간 임피던스였던 출력이 제 값으로 출력을 하는 것을 확인할 수 있다.
'Language > Verilog' 카테고리의 다른 글
Vivado : Basys3 FND / 디지털 시계, 분주 (0) | 2024.07.17 |
---|---|
Vivado : Basys3 7segment (0) | 2024.07.16 |
Vivado : 동기식 카운터 (0) | 2024.07.12 |
Vivado : Decoder, Encoder (2) | 2024.06.25 |
Vivado : 4bit 가산기 (0) | 2024.06.16 |