uart_tx
`timescale 1ns / 1ps
module uart_tx ( // PC
input clk,
input reset,
input start,
input [7:0] tx_data,
output tx,
output tx_done
);
wire w_br_tick;
baudrate_generator U_BAUDRATE_GEN (
.clk(clk),
.reset(reset),
.br_tick(w_br_tick)
);
transmitter U_Transmitter (
.clk(clk),
.reset(reset),
.br_tick(w_br_tick),
.start(start),
.tx_data(tx_data),
.tx(tx),
.tx_done(tx_done)
);
endmodule
module baudrate_generator (
input clk,
input reset,
output br_tick
);
// 16 bit sampling -> baudrate 16 times
reg [$clog2(100_000_000 / 9600 / 16)-1 : 0] counter_reg, counter_next;
reg tick_reg, tick_next;
assign br_tick = tick_reg;
always @(posedge clk, posedge reset) begin
if (reset) begin
counter_reg <= 0;
tick_reg <= 1'b0;
end else begin
counter_reg <= counter_next;
tick_reg <= tick_next;
end
end
always @(*) begin
counter_next = counter_reg;
//if (counter_reg == 100_000_000 / 9600 / 16 - 1) begin
if (counter_reg == 3) begin // for simulation
counter_next = 0;
tick_next = 1'b1;
end else begin
counter_next = counter_reg + 1;
tick_next = 1'b0;
end
end
endmodule
module transmitter (
input clk,
input reset,
input br_tick,
input start,
input [7:0] tx_data,
output tx,
output tx_done
);
localparam IDLE = 0, START = 1, DATA = 2, STOP = 3;
reg [1:0] state, state_next;
reg tx_reg, tx_next;
reg tx_done_reg, tx_done_next;
reg [7:0] data_tmp_reg, data_tmp_next;
reg [3:0] br_cnt_reg, br_cnt_next;
reg [2:0] data_bit_cnt_reg, data_bit_cnt_next;
assign tx = tx_reg;
assign tx_done = tx_done_reg;
always @(posedge clk, posedge reset) begin
if (reset) begin
state <= IDLE;
tx_reg <= 1'b0;
br_cnt_reg <= 0;
data_bit_cnt_reg <= 0;
data_tmp_reg <= 0;
//tx_done_reg <= 1'b0;
end else begin
state <= state_next;
tx_reg <= tx_next;
br_cnt_reg <= br_cnt_next;
data_bit_cnt_reg <= data_bit_cnt_next;
data_tmp_reg <= data_tmp_next;
//tx_done_reg <= tx_done_next;
end
end
always @(*) begin
state_next = state;
data_tmp_next = data_tmp_reg;
tx_next = tx_reg;
br_cnt_next = br_cnt_reg;
data_bit_cnt_next = data_bit_cnt_reg;
//tx_done_next = tx_done_reg;
case (state)
IDLE: begin
tx_done_reg = 1'b0;
tx_next = 1'b1;
if (start) begin
state_next = START;
data_tmp_next = tx_data;
br_cnt_next = 0; // baud rate 16bit sampling
data_bit_cnt_next = 0;
end
end
START: begin
tx_next = 1'b0;
if (br_tick) begin
if (br_cnt_reg == 15) begin
state_next = DATA;
br_cnt_next = 0;
end else begin
br_cnt_next = br_cnt_reg + 1;
end
end
end
DATA: begin
tx_next = data_tmp_reg[0];
if (br_tick) begin
if (br_cnt_reg == 15) begin
if (data_bit_cnt_reg == 7) begin
state_next = STOP;
br_cnt_next = 0;
end else begin
data_bit_cnt_next = data_bit_cnt_reg + 1;
data_tmp_next = {1'b0, data_tmp_reg[7:1]};
br_cnt_next = 0;
end
end else begin
br_cnt_next = br_cnt_reg + 1;
end
end
end
STOP: begin
tx_next = 1'b1;
if (br_tick) begin
if (br_cnt_reg == 15) begin
tx_done_reg = 1'b1;
state_next = IDLE;
end else begin
br_cnt_next = br_cnt_reg + 1;
end
end
end
endcase
end
endmodule
schematic
tb_uart_tx.sv
`timescale 1ns / 1ps
interface tr_interface;
logic clk;
logic reset;
logic start;
logic [7:0] tx_data;
logic tx;
logic tx_done;
endinterface
class transaction;
rand logic [7:0] tx_data;
logic tx;
logic tx_done;
task display(string name);
$display("[%s] tx_data: %b, tx: %b, tx_done: %b", name, tx_data, tx,
tx_done);
endtask
endclass
class generator;
transaction trans;
mailbox #(transaction) gen2drv_mbox;
event gen_next_event;
function new(mailbox#(transaction) gen2drv_mbox, event gen_next_event);
this.gen2drv_mbox = gen2drv_mbox;
this.gen_next_event = gen_next_event;
endfunction //new()
task run(int count);
repeat (count) begin
trans = new();
assert (trans.randomize())
else $error("[GEN] trans.randomize() error!");
gen2drv_mbox.put(trans);
trans.display("GEN");
@(gen_next_event);
end
endtask
endclass //generator
class driver;
transaction trans;
mailbox #(transaction) gen2drv_mbox;
virtual tr_interface tr_intf;
reg [7:0] data;
integer count;
function new(virtual tr_interface tr_intf,
mailbox#(transaction) gen2drv_mbox);
this.tr_intf = tr_intf;
this.gen2drv_mbox = gen2drv_mbox;
count = 0;
endfunction //new()
task reset();
tr_intf.tx_data <= 0;
tr_intf.reset <= 1'b1;
tr_intf.start <= 1'b1;
repeat (5) @(posedge tr_intf.clk);
tr_intf.reset <= 1'b0;
endtask
task run();
forever begin
gen2drv_mbox.get(trans);
if (count % 10 == 0) begin
tr_intf.start = 1'b1;
data = trans.tx_data;
tr_intf.tx_data = data;
@(posedge tr_intf.clk);
tr_intf.start = 1'b0;
// $display("IDLE");
end else if (count % 10 == 9) begin
// $display("STOP");
end else begin
tr_intf.tx_data = data;
end
count++;
trans.display("DRV");
//->mon_Next_Event;
end
endtask
endclass //driver
class monitor;
virtual tr_interface tr_intf;
mailbox #(transaction) mon2scb_mbox;
transaction trans;
//event mon_Next_Event;
function new(virtual tr_interface tr_intf,
mailbox#(transaction) mon2scb_mbox);
this.tr_intf = tr_intf;
this.mon2scb_mbox = mon2scb_mbox;
endfunction //new()
task run();
forever begin
//@(mon_Next_Event);
@(posedge tr_intf.clk); //drvier랑 timing 맞춤
trans = new();
trans.tx_data = tr_intf.tx_data;
repeat (32) @(posedge tr_intf.clk);
trans.tx = tr_intf.tx;
repeat (32) @(posedge tr_intf.clk);
mon2scb_mbox.put(trans);
trans.display("MON");
end
endtask
endclass //monitor
class scoreboard;
mailbox #(transaction) mon2scb_mbox;
transaction trans;
event gen_next_event;
reg [7:0] tx_reg;
int total_cnt, pass_cnt, fail_cnt;
function new(mailbox#(transaction) mon2scb_mbox, event gen_next_event);
this.mon2scb_mbox = mon2scb_mbox;
this.gen_next_event = gen_next_event;
total_cnt = 0;
pass_cnt = 0;
fail_cnt = 0;
tx_reg = 0;
endfunction //new()
task run();
forever begin
mon2scb_mbox.get(trans);
tx_reg = {trans.tx, tx_reg[7:1]};
trans.display("SCB");
if (total_cnt % 10 == 8) begin
if (trans.tx_data == tx_reg) begin
$display(" --> TRANSMIT PASS! %b == %b", trans.tx_data,
tx_reg);
pass_cnt++;
end else begin
$display(" --> TRANSMIT FAIL! %b == %b", trans.tx_data,
tx_reg);
fail_cnt++;
end
end
total_cnt++;
->gen_next_event;
end
endtask
endclass //scoreboard
class environment;
generator gen;
driver drv;
monitor mon;
scoreboard scb;
event gen_next_event;
mailbox #(transaction) gen2drv_mbox;
mailbox #(transaction) mon2scb_mbox;
function new(virtual tr_interface tr_intf);
gen2drv_mbox = new();
mon2scb_mbox = new();
gen = new(gen2drv_mbox, gen_next_event);
drv = new(tr_intf, gen2drv_mbox);
mon = new(tr_intf, mon2scb_mbox);
scb = new(mon2scb_mbox, gen_next_event);
endfunction
task report();
$display("================================");
$display("== Final Report ==");
$display("================================");
$display("Total Test : %d", scb.total_cnt);
$display("Pass Count : %d", scb.pass_cnt);
$display("Fail Count : %d", scb.fail_cnt);
$display("================================");
$display("== test bench is finished! ==");
$display("================================");
endtask
task pre_run();
drv.reset();
endtask
task run();
fork
gen.run(1000);
drv.run();
mon.run();
scb.run();
join_any
report();
#10 $finish;
endtask
task run_test();
pre_run();
run();
endtask
endclass
module tb_transmitter ();
environment env;
tr_interface tr_intf ();
transmitter dut (
.clk(tr_intf.clk),
.reset(tr_intf.reset),
.start(tr_intf.start),
.tx_data(tr_intf.tx_data),
.tx(tr_intf.tx),
.tx_done(tr_intf.tx_done)
);
always #5 tr_intf.clk = ~tr_intf.clk; //5nsec 간격으로 toggle
initial begin
tr_intf.clk = 0;
end
initial begin
env = new(tr_intf);
env.run_test();
end
endmodule
simulation
...
uart_rx
`timescale 1ns / 1ps
module uart_rx ( // PC
input clk,
input reset,
input rx,
output [7:0] rx_data,
output rx_done
);
wire w_br_tick;
baudrate_generator U_BAUDRATE_GEN (
.clk(clk),
.reset(reset),
.br_tick(w_br_tick)
);
receiver U_Receiver (
.clk(clk),
.reset(reset),
.br_tick(w_br_tick),
.rx(rx),
.rx_data(rx_data),
.rx_done(rx_done)
);
endmodule
module baudrate_generator (
input clk,
input reset,
output br_tick
);
// 16 bit sampling -> baudrate 16 times
reg [$clog2(100_000_000 / 9600 / 16)-1 : 0] counter_reg, counter_next;
reg tick_reg, tick_next;
assign br_tick = tick_reg;
always @(posedge clk, posedge reset) begin
if (reset) begin
counter_reg <= 0;
tick_reg <= 1'b0;
end else begin
counter_reg <= counter_next;
tick_reg <= tick_next;
end
end
always @(*) begin
counter_next = counter_reg;
//if (counter_reg == 100_000_000 / 9600 / 16 - 1) begin
if (counter_reg == 3) begin // for simulation
counter_next = 0;
tick_next = 1'b1;
end else begin
counter_next = counter_reg + 1;
tick_next = 1'b0;
end
end
endmodule
module receiver (
input clk,
input reset,
input br_tick,
input rx,
output [7:0] rx_data,
output rx_done
);
localparam IDLE = 0, START = 1, DATA = 2, STOP = 3;
reg [1:0] state, state_next;
reg [7:0] rx_data_reg, rx_data_next;
reg rx_done_reg, rx_done_next;
reg [3:0] br_cnt_reg, br_cnt_next;
reg [2:0] data_bit_cnt_reg, data_bit_cnt_next;
assign rx_data = rx_data_reg;
assign rx_done = rx_done_reg;
always @(posedge clk, posedge reset) begin
if (reset) begin
state <= IDLE;
rx_data_reg <= 0;
rx_done_reg <= 1'b0;
br_cnt_reg <= 0;
data_bit_cnt_reg <= 0;
end else begin
state <= state_next;
rx_data_reg <= rx_data_next;
rx_done_reg <= rx_done_next;
br_cnt_reg <= br_cnt_next;
data_bit_cnt_reg <= data_bit_cnt_next;
end
end
always @(*) begin
state_next = state;
br_cnt_next = br_cnt_reg;
data_bit_cnt_next = data_bit_cnt_reg;
rx_data_next = rx_data_reg;
rx_done_next = rx_done_reg;
case (state)
IDLE: begin
rx_done_next = 1'b0;
if (rx == 1'b0) begin
br_cnt_next = 0;
data_bit_cnt_next = 0;
rx_data_next = 0;
state_next = START;
end
end
START: begin
if (br_tick) begin
if (br_cnt_reg == 7) begin
br_cnt_next = 0;
state_next = DATA;
end else begin
br_cnt_next = br_cnt_reg + 1;
end
end
end
DATA: begin
if (br_tick) begin
if (br_cnt_reg == 15) begin
br_cnt_next = 0;
rx_data_next = {rx, rx_data_reg[7:1]};
if (data_bit_cnt_reg == 7) begin
state_next = STOP;
br_cnt_next = 0;
end else begin
data_bit_cnt_next = data_bit_cnt_reg + 1;
end
end else begin
br_cnt_next = br_cnt_reg + 1;
end
end
end
STOP: begin
if (br_tick) begin
if (br_cnt_reg == 15) begin
br_cnt_next = 0;
rx_done_next = 1'b1;
state_next = IDLE;
end else begin
br_cnt_next = br_cnt_reg + 1;
end
end
end
endcase
end
endmodule
schematic
tb_uart_rx.sv
`timescale 1ns / 1ps
interface rx_interface;
logic clk;
logic reset;
logic rx;
logic [7:0] rx_data;
logic rx_done;
endinterface
class transaction;
rand logic rx;
logic [7:0] rx_data;
logic rx_done;
task display(string name);
$display("[%s] rx_data: %b, rx: %b, rx_done: %b", name, rx_data, rx,
rx_done);
endtask
endclass
class generator;
transaction trans;
mailbox #(transaction) gen2drv_mbox;
event gen_next_event;
function new(mailbox#(transaction) gen2drv_mbox, event gen_next_event);
this.gen2drv_mbox = gen2drv_mbox;
this.gen_next_event = gen_next_event;
endfunction //new()
task run(int count);
repeat (count) begin
trans = new();
assert (trans.randomize())
else $error("[GEN] trans.randomize() error!");
gen2drv_mbox.put(trans);
trans.display("GEN");
@(gen_next_event);
end
endtask
endclass //generator
class driver;
transaction trans;
mailbox #(transaction) gen2drv_mbox;
virtual rx_interface rx_intf;
integer count;
function new(virtual rx_interface rx_intf,
mailbox#(transaction) gen2drv_mbox);
this.rx_intf = rx_intf;
this.gen2drv_mbox = gen2drv_mbox;
count = 0;
endfunction //new()
task reset();
rx_intf.rx <= 1'b1;
rx_intf.reset <= 1'b1;
repeat (5) @(rx_intf.clk);
rx_intf.reset <= 1'b0;
endtask
task run();
forever begin
gen2drv_mbox.get(trans); // Get the transaction from the mailbox
if (count % 10 == 0) begin
//rx_intf.rx <= 1'b1;
//@(posedge rx_intf.clk);
rx_intf.rx <= 1'b0;
$display("START");
end else if (count % 10 == 9) begin
rx_intf.rx <= 1'b1; // Stop bit
$display("STOP");
end else begin
rx_intf.rx = trans.rx; // Data bits from transaction
end
count++;
repeat (64) @(posedge rx_intf.clk);
trans.display("DRV"); // Display the current transaction details
end
endtask
endclass //driver
class monitor;
virtual rx_interface rx_intf;
mailbox #(transaction) mon2scb_mbox;
transaction trans;
function new(virtual rx_interface rx_intf,
mailbox#(transaction) mon2scb_mbox);
this.rx_intf = rx_intf;
this.mon2scb_mbox = mon2scb_mbox;
endfunction //new()
task run();
forever begin
trans = new();
//@(posedge rx_intf.clk);
trans.rx = rx_intf.rx;
repeat (32) @(posedge rx_intf.clk);
trans.rx_data = rx_intf.rx_data;
repeat (32) @(posedge rx_intf.clk);
trans.rx_done = rx_intf.rx_done;
mon2scb_mbox.put(trans);
trans.display("MON");
end
endtask
endclass //monitor
class scoreboard;
mailbox #(transaction) mon2scb_mbox;
transaction trans;
event gen_next_event;
logic [7:0] rx_reg;
int total_cnt, pass_cnt, fail_cnt;
function new(mailbox#(transaction) mon2scb_mbox, event gen_next_event);
this.mon2scb_mbox = mon2scb_mbox;
this.gen_next_event = gen_next_event;
total_cnt = 0;
pass_cnt = 0;
fail_cnt = 0;
rx_reg = 0;
endfunction //new()
task run();
forever begin
mon2scb_mbox.get(trans);
rx_reg = {trans.rx, rx_reg[7:1]};
trans.display("SCB");
if (total_cnt % 10 == 8) begin
if (trans.rx_data == rx_reg) begin
$display(" --> PASS! %b == %b", trans.rx_data, rx_reg);
pass_cnt++;
end else begin
$display(" --> FAIL! %b == %b", trans.rx_data, rx_reg);
fail_cnt++;
end
end
total_cnt++;
->gen_next_event;
end
endtask
endclass //scoreboard
class environment;
generator gen;
driver drv;
monitor mon;
scoreboard scb;
event gen_next_event;
mailbox #(transaction) gen2drv_mbox;
mailbox #(transaction) mon2scb_mbox;
function new(virtual rx_interface rx_intf);
gen2drv_mbox = new();
mon2scb_mbox = new();
gen = new(gen2drv_mbox, gen_next_event);
drv = new(rx_intf, gen2drv_mbox);
mon = new(rx_intf, mon2scb_mbox);
scb = new(mon2scb_mbox, gen_next_event);
endfunction
task report();
$display("================================");
$display("== Final Report ==");
$display("================================");
$display("Total Test : %d", scb.total_cnt);
$display("Pass Count : %d", scb.pass_cnt);
$display("Fail Count : %d", scb.fail_cnt);
$display("================================");
$display("== test bench is finished! ==");
$display("================================");
endtask
task pre_run();
drv.reset();
endtask
task run();
fork
gen.run(1000);
drv.run();
mon.run();
scb.run();
join_any
report();
#10 $finish;
endtask
task run_test();
pre_run();
run();
endtask
endclass
module tb_receiver ();
environment env;
rx_interface rx_intf ();
uart_rx dut (
.clk(rx_intf.clk),
.reset(rx_intf.reset),
.rx_data(rx_intf.rx_data),
.rx(rx_intf.rx),
.rx_done(rx_intf.rx_done)
);
always #5 rx_intf.clk = ~rx_intf.clk;
initial begin
rx_intf.clk = 0;
end
initial begin
env = new(rx_intf);
env.run_test();
end
endmodule
simulation
...
'[하만]세미콘 아카데미 > verilog' 카테고리의 다른 글
0528 Dedicated Process num55 (0) | 2024.07.09 |
---|---|
0528 Computer Architecture, Dedicated Processor 0~9 counter (0) | 2024.07.09 |
0524 uart fifo (0) | 2024.07.09 |
0523 FIFO Systemverilog verification (0) | 2024.07.09 |
0522 RAM SystemVerilog verification (0) | 2024.07.09 |