Verilog HDL로 각 모듈을 하나씩 만들어 보자.
첫 번째로 보레이트 생성기를 만들어 보자.
먼저 각 모듈에서 사용할 매크로를 정의하자.
`ifndef __DEFINES_VH__
`define __DEFINES_VH__
// States of state machine
`define STATE_RESET 3'b001
`define STATE_IDLE 3'b010
`define STATE_START_BIT 3'b011
`define STATE_DATA_BITS 3'b100
`define STATE_STOP_BIT 3'b101
// Main clocks
`define CLOCK_RATE_100MHZ 100000000
// Baudrates
`define BAUD_RATE_1200 1200
`define BAUD_RATE_9600 9600
`define BAUD_RATE_57600 57600
`define BAUD_RATE_115200 115200
`define BAUD_RATE_230400 230400
`define BAUD_RATE_460800 460800
// Oversampling rates
`define OVERSAMPLING_1 1
`define OVERSAMPLING_2 2
`define OVERSAMPLING_4 4
`define OVERSAMPLING_8 8
`define OVERSAMPLING_16 16
// Data bits
`define DATA_BITS_7 7
`define DATA_BITS_8 8
`endif /*__DEFINES_VH__*/
입력 클럭, 보레이트, 오버샘플링 등 사용자가 가변적으로 설정할 수 있는 값들을 따로 매크로 파일로 빼서 관리하도록 구성하였다.
RTL 모듈을 만들어 보자.
`ifndef __BAUDRATE_GENERATOR_V__
`define __BAUDRATE_GENERATOR_V__
`include "inc/Defines.vh"
module BaudRateGenerator #(
parameter CLOCK_RATE = `CLOCK_RATE_100MHZ, // use 100MHz as default
parameter BAUD_RATE = `BAUD_RATE_115200, // use 115200 as default
parameter OVERSAMPLING = `OVERSAMPLING_16 // use 16x as default
)(
input wire clk, // main clock
output reg rxClk, // baud rate for rx
output reg txClk // baud rate for tx
);
localparam MAX_RATE_RX = CLOCK_RATE / (2 * BAUD_RATE * OVERSAMPLING);
localparam MAX_RATE_TX = CLOCK_RATE / (2 * BAUD_RATE);
localparam RX_CNT_WIDTH = $clog2(MAX_RATE_RX);
localparam TX_CNT_WIDTH = $clog2(MAX_RATE_TX);
reg [RX_CNT_WIDTH - 1:0] rxCnt = 0;
reg [TX_CNT_WIDTH - 1:0] txCnt = 0;
initial begin
rxClk = 1'b0;
txClk = 1'b0;
end
always @ (posedge clk) begin
// generate rx clock
if (rxCnt == MAX_RATE_RX) begin
rxCnt <= 0;
rxClk <= ~rxClk;
end else begin
rxCnt <= rxCnt + 1'b1;
end
// generate tx clock
if (txCnt == MAX_RATE_TX) begin
txCnt <= 0;
txClk <= ~txClk;
end else begin
txCnt <= txCnt + 1'b1;
end
end
endmodule
`endif /*__BAUDRATE_GENERATOR_V__*/
입력 클럭과 보레이트, 오버샘플링 값을 이용하여 rx, tx 클럭 주기를 계산한 후, 카운터를 이용하여 rx, tx 클럭을 분주시킨다.
위 모듈을 테스트하기 위한 테스트벤치를 만들어 보자.
`timescale 10ns/1ns
`include "inc/Defines.vh"
`include "src/BaudRateGenerator.v"
module tbBaudRateGenerator();
parameter CLOCK_RATE = `CLOCK_RATE_100MHZ;
parameter BAUD_RATE = `BAUD_RATE_115200;
parameter OVERSAMPLING = `OVERSAMPLING_16;
reg clk = 0;
wire rxClk;
wire txClk;
BaudRateGenerator #(
.CLOCK_RATE(CLOCK_RATE),
.BAUD_RATE(BAUD_RATE),
.OVERSAMPLING(OVERSAMPLING)
) test (
.clk(clk),
.rxClk(rxClk),
.txClk(txClk)
);
initial begin
$dumpfile("test.vcd");
$dumpvars(-1, test);
end
always begin
#0.5 clk = ~clk;
end
initial begin
#1000 $finish;
end
endmodule
10ns당 1회 분주하기 때문에 clk에 100MHz 클럭이 만들어진다. 이를 시뮬레이션 돌리면 이에 대한 rx, tx 클럭이 출력되는 것을 기대할 수 있다.
개발 편의를 위해 Makefile을 생성하였다.
all: BaudRateGenerator
BaudRateGenerator: clean
iverilog -o test.vvp testbench/BaudRateGenerator_tb.v
vvp test.vvp
gtkwave test.vcd
clean:
rm -f test.vcd
rm -f test.vvp
make만 입력하면 자동으로 clean 후 시뮬레이션을 돌릴 수 있다.
make를 입력한다.
make
그러면 gtkwave가 실행되며, 아래와 같이 입출력을 확인할 수 있다.
먼저 rx 클럭부터 보자.
입력 클럭이 100MHz이고 보레이트 115200에 오버샘플링이 16x이기 때문에, rx 분주를 계산하는 공식은 아래와 같다.
rxClk = 100000000 / (2 * 115200 * 16) = 27.16
다시 말하면 clk이 27회 분주하면 rxClk이 토글되며, clk이 54회 분주하면 rxClk이 1회 분주한다.
rxClk이 LOW에서 HIGH로 갈 때까지의 clk 분주 개수를 세어보면 27개인 것을 알 수 있다.
따라서 입력 클럭에 맞춰 rx 클럭이 정상적으로 생성된 것을 볼 수 있다.
그다음 tx 클럭을 보자.
입력 클럭이 너무 촘촘해서 잘 안보이기 때문에, rx 클럭과 비교해보자.
rx 클럭이 16회 분주할 때마다 tx 클럭이 1회 분주하는지를 보면 되며,
위 그림을 통해 tx 클럭이 정상적으로 생성되는 것을 볼 수 있다.