- 이전 글
(1) RTL : https://chonh0531.tistory.com/5
목차
1. 과제 개요
2. 소스코드
3. 실습 결과
1. 과제 개요
이전에 구현해 본 UART_RX를 테스트해 보기로 합니다.
UART 신호 생성은 ARM Cortex-M4 Core가 있는 NUCLEO-F429ZI 보드를 이용하여
FPGA에서 LCD를 제어해 보도록 합니다.
1. 컴퓨터에서 UART 통신으로 Nucleo 보드에 명령을 전달
2. Nucleo 보드는 FPGA에 다시 UART 통신으로 명령을 전달
3. FPGA는 data 부분의 마지막 4비트를 모터 속도제어에 이용
2. 소스코드
- Top 모듈 Uart_lcd
`timescale 1ns / 1ps
module uart_lcd(
input clk,
input rst,
input rx,
output [7:0]data,
output lcd_e,
output lcd_rs
);
wire rx_clk;
wire [7:0]rx_register;
wire [4:0]clk_cnt;
wire [3:0]rx_cnt;
wire [7:0]lcd_register;
wire [6:0]lcd_cnt;
uart_clock #(.max(163)) C0 (.clk_in(clk),.rst(rst),.clk_out(rx_clk));
uart_rx R0 (.clk(rx_clk),.rst(rst),.rx(rx),.rx_register(rx_register),.clk_cnt(clk_cnt),.rx_cnt(rx_cnt));
lcd L0 (.clk(clk),.rx_clk(rx_clk),.rst(rst),.rx_register(rx_register),.rx_cnt(rx_cnt),.clk_cnt(clk_cnt),.lcd_e(lcd_e),.lcd_rs(lcd_rs),.data(data),.lcd_cnt(lcd_cnt),.lcd_register(lcd_register));
ila_0 ila_0 (.clk(clk),.probe0(lcd_cnt),.probe1(rx),.probe2(data),.probe3(lcd_register),.probe4(rx_cnt));
endmodule
- UART_RX
module uart_rx(
input clk,
input rx,
input rst,
output reg [7:0]rx_register,
output reg rx_end,
output reg [4:0]clk_cnt,
output reg [3:0]rx_cnt
);
localparam RX_IDLE = 2'b00;
localparam RX_START = 2'b01;
localparam RX_STOP = 2'b10;
reg [1:0]current_state ;
reg [1:0]next_state;
reg [15:0]rx_check;
always @(posedge clk or posedge rst) begin
if (rst)
clk_cnt <= 0;
else if (clk_cnt == 16 && rx_cnt != 11)
clk_cnt <= 1;
else if (clk_cnt == 16 && rx_cnt == 11)
clk_cnt <= 0;
else
case (next_state)
RX_IDLE : clk_cnt <= 0;
RX_START : clk_cnt <= clk_cnt+1;
RX_STOP : clk_cnt <= clk_cnt+1;
default : clk_cnt <= clk_cnt;
endcase
end
always @(posedge clk or posedge rst) begin
if (rst)
rx_cnt <= 0;
else if (rx_cnt == 11 && clk_cnt == 16)
rx_cnt <= 0;
else if (current_state == RX_IDLE && rx == 0)
rx_cnt <= 1;
else if (clk_cnt == 16)
rx_cnt <= rx_cnt+1;
else
rx_cnt <= rx_cnt;
end
always @(posedge clk or posedge rst) begin
if (rst)
current_state <= RX_IDLE;
else
current_state <= next_state;
end
always @(negedge clk or posedge rst) begin
if (rst)
next_state <= RX_IDLE;
else
case(current_state)
RX_IDLE : if (rx == 0)
next_state <= RX_START;
else
next_state <= next_state;
RX_START : if (rx_cnt == 11)
next_state <= RX_STOP;
else
next_state <= next_state;
RX_STOP : if (rx_cnt == 0)
next_state <= RX_IDLE;
else
next_state <= next_state;
default : next_state <= next_state;
endcase
end
reg [4:0]rx_score;
always @(posedge clk or posedge rst) begin
if (rst)
rx_check <= 16'b1111_1111_1111_1111;
else
rx_check <= {rx_check[15:0], rx};
end
always @(posedge clk or posedge rst) begin
if (rst)
rx_score <= 0;
else if (rx_cnt == 0)
rx_score <= 0;
else if (clk_cnt ==16)
rx_score <= 0;
else if (rx_cnt == 11)
rx_score <= 0;
else if (rx == 1)
rx_score <= rx_score+1;
else
rx_score <= rx_score;
end
reg rx_sampling;
always @(posedge clk or posedge rst) begin
if (rst)
rx_sampling <= 1;
else if (rx_cnt == 0)
rx_sampling <= 1;
else if (clk_cnt == 16 && rx_score > 8 && rx_cnt != 11)
rx_sampling <= 1;
else if (clk_cnt == 16 && rx_score < 8 && rx_cnt != 11)
rx_sampling <= 0;
else
rx_sampling <= rx_sampling;
end
reg [9:0]rx_received;
always @(posedge clk or posedge rst) begin
if (rst)
rx_received <= 10'b00_0000_0000;
else if (clk_cnt == 1 && rx_cnt >1)
rx_received <= {rx_received[8:0], rx_sampling};
else
rx_received <= rx_received;
end
always @(posedge clk or posedge rst) begin
if (rst)
rx_register <= 8'b0000_0000;
else if (rx_cnt == 11 && clk_cnt == 2)
rx_register <= rx_received[8:1];
else
rx_register <= rx_register;
end
always @(posedge clk or posedge rst) begin
if (rst)
rx_end <= 1;
else
case (next_state)
RX_IDLE : rx_end <= 1;
RX_START : rx_end <= 0;
RX_STOP : rx_end <= 0;
default : rx_end <= rx_end;
endcase
end
endmodule
이전 포스팅에서 언급하였듯이,
코드를 최적화하지 않고 직관적으로 코딩한 후 그대로 두었습니다.
- UART_Clock
`timescale 1ns / 1ps
module uart_clock(
input clk_in,
input rst,
output reg clk_out
);
reg [10:0]clk_cnt;
parameter max = 1;
always @(posedge clk_in or posedge rst) begin
if (rst) begin
clk_cnt <= 0;
end
else if (clk_cnt == max) begin
clk_cnt <= 0;
end
else
clk_cnt <= clk_cnt + 1;
end
always @(posedge clk_in or posedge rst) begin
if (rst) begin
clk_out <= 0;
end
else if (clk_cnt == max) begin
clk_out <= !clk_out;
end
else
clk_out <= clk_out;
end
endmodule
- LCD
`timescale 1ns / 1ps
module lcd(
input clk,
input rx_clk,
input rst,
input [7:0]rx_register,
input [3:0]rx_cnt,
input [4:0]clk_cnt,
output reg lcd_e,
output reg lcd_rs,
output reg [7:0]data,
output reg [6:0]lcd_cnt,
output reg [7:0]lcd_register
);
reg [7:0] dspdata [0:37];
integer i = 0;
integer j = 0;
always @(posedge rx_clk or posedge rst) begin
if (rst)
lcd_register <= 8'h80;
else if (rx_cnt == 11 && clk_cnt == 16) begin
lcd_register[0] <= rx_register[7];
lcd_register[1] <= rx_register[6];
lcd_register[2] <= rx_register[5];
lcd_register[3] <= rx_register[4];
lcd_register[4] <= rx_register[3];
lcd_register[5] <= rx_register[2];
lcd_register[6] <= rx_register[1];
lcd_register[7] <= rx_register[0];
end
else
lcd_register <= lcd_register;
end
always @(posedge rx_clk or posedge rst) begin
if (rst)
lcd_cnt <= 4;
else if (lcd_cnt == 20 && rx_cnt == 11 && clk_cnt == 16)
lcd_cnt <= 22;
else if (lcd_cnt == 37 && rx_cnt == 11 && clk_cnt == 16)
lcd_cnt <= 5;
else if (rx_cnt== 11 && clk_cnt == 16)
lcd_cnt <= lcd_cnt+1;
else
lcd_cnt <= lcd_cnt;
end
always @(posedge clk or posedge rst) begin
if (rst) begin
dspdata[0] <= 8'b00111000; // function set 8bit, 2line, 5x7 dot
dspdata[1] <= 8'b00001100; // display on/off , display on, cursor off, cursor blink off
dspdata[2] <= 8'b00000110; // entry mode set increment cursor position, no display shift
dspdata[3] <= 8'b00000001; // clear display
dspdata[4] <= 8'h80; // set cg ram address 1000 0000 1번라인 첫번째부터
dspdata[5] <= 8'b00110000; // 0
dspdata[6] <= 8'b00110000; // 0
dspdata[7] <= 8'b00110000; // 0
dspdata[8] <= 8'b00110000; // 0
dspdata[9] <= 8'b00110000; // 0
dspdata[10] <= 8'b00110000; // 0
dspdata[11] <= 8'b00110000; // 0
dspdata[12] <= 8'b00110000; // 0
dspdata[13] <= 8'b00110000; // 0
dspdata[14] <= 8'b00110000; // 0
dspdata[15] <= 8'b00110000; // 0
dspdata[16] <= 8'b00110000; // 0
dspdata[17] <= 8'b00110000; // 0
dspdata[18] <= 8'b00110000; // 0
dspdata[19] <= 8'b00110000; // 0
dspdata[20] <= 8'b00110000; // 0
dspdata[21] <= 8'hC0; // set cg ram address 1100 0000 2번라인 첫번째부터
dspdata[22] <= 8'b00110000; // 0
dspdata[23] <= 8'b00110000; // 0
dspdata[24] <= 8'b00110000; // 0
dspdata[25] <= 8'b00110000; // 0
dspdata[26] <= 8'b00110000; // 0
dspdata[27] <= 8'b00110000; // 0
dspdata[28] <= 8'b00110000; // 0
dspdata[29] <= 8'b00110000; // 0
dspdata[30] <= 8'b00110000; // 0
dspdata[31] <= 8'b00110000; // 0
dspdata[32] <= 8'b00110000; // 0
dspdata[33] <= 8'b00110000; // 0
dspdata[34] <= 8'b00110000; // 0
dspdata[35] <= 8'b00110000; // 0
dspdata[36] <= 8'b00110000; // 0
dspdata[37] <= 8'b00110000; // 0
end
else if (rx_cnt == 0)
dspdata[lcd_cnt] <= lcd_register;
else begin
dspdata[0] <= dspdata[0];
dspdata[1] <= dspdata[1];
dspdata[2] <= dspdata[2];
dspdata[3] <= dspdata[3];
dspdata[4] <= dspdata[4];
dspdata[5] <= dspdata[5];
dspdata[6] <= dspdata[6];
dspdata[7] <= dspdata[7];
dspdata[8] <= dspdata[8];
dspdata[9] <= dspdata[9];
dspdata[10] <= dspdata[10];
dspdata[11] <= dspdata[11];
dspdata[12] <= dspdata[12];
dspdata[13] <= dspdata[13];
dspdata[14] <= dspdata[14];
dspdata[15] <= dspdata[15];
dspdata[16] <= dspdata[16];
dspdata[17] <= dspdata[17];
dspdata[18] <= dspdata[18];
dspdata[19] <= dspdata[19];
dspdata[20] <= dspdata[20];
dspdata[21] <= dspdata[21];
dspdata[22] <= dspdata[22];
dspdata[23] <= dspdata[23];
dspdata[24] <= dspdata[24];
dspdata[25] <= dspdata[25];
dspdata[26] <= dspdata[26];
dspdata[27] <= dspdata[27];
dspdata[28] <= dspdata[28];
dspdata[29] <= dspdata[29];
dspdata[30] <= dspdata[30];
dspdata[31] <= dspdata[31];
dspdata[32] <= dspdata[32];
dspdata[33] <= dspdata[33];
dspdata[34] <= dspdata[34];
dspdata[35] <= dspdata[35];
dspdata[36] <= dspdata[36];
dspdata[37] <= dspdata[37];
end
end
always @(posedge clk or posedge rst) begin
if (rst) begin
i <= 0;
lcd_e <= 0;
data <= 8'b0;
end
else begin
if(i <= 1000000) begin
i <= i + 1;
lcd_e <= 1;
data <= dspdata[j];
end
else if ((i > 1000000) && (i < 2000000)) begin
i <= i + 1;
lcd_e <= 0;
end
else if (i == 2000000) begin
i <= 1'b0;
end
else begin
i <= i;
lcd_e <= lcd_e;
data <= data;
end
end
end
always @(posedge clk or posedge rst) begin
if (rst)
j <= 0;
else if (j == 38)
j <= 4;
else if (i == 2000000)
j <= j+1;
else
j <= j;
end
always @(posedge clk or posedge rst) begin
if (rst)
lcd_rs <= 0;
else if (j <= 4)
lcd_rs <= 0;
else if (j > 4 && j < 21)
lcd_rs <= 1;
else if (j == 21)
lcd_rs <= 0;
else if (j > 21 && j < 38)
lcd_rs <= 1;
else
lcd_rs <= lcd_rs;
end
endmodule
- Nucleo Board 제어용 코드
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "string.h"
/* USER CODE END Includes */
/* USER CODE BEGIN PV */
uint8_t rx_data;
int led_mode = 0;
/* USER CODE END PV */
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART3_UART_Init();
/* Initialize interrupts */
MX_NVIC_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart3, (uint8_t *)&rx_data, 1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
switch (led_mode)
{
case 1 : GPIOB -> ODR = 0x0001; break;
case 2 : GPIOB -> ODR = 0x0080; break;
case 3 : GPIOB -> ODR = 0x4000; break;
case 0 : GPIOB -> ODR = 0x0000; break;
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart -> Instance == USART3)
{
HAL_UART_Receive_IT(&huart3, (uint8_t *)&rx_data, 1);
HAL_UART_Transmit(&huart3, (uint8_t *)&rx_data, 1, 500);
switch (rx_data)
{
case '1': led_mode = 1; break;
case '2': led_mode = 2; break;
case '3': led_mode = 3; break;
default: led_mode = 0;
}
}
}
/* USER CODE END 4 */
CUBE IDE를 다루는 법은 본 글의 목적에 맞지 않으므로
자세히 설명하지 않고, 필요한 부분만 적어두었습니다.
led 부분의 경우 uart 통신이 잘 이루어지는지 확인하기 위해 넣었습니다.
3. 실습결과
FPGA는 ZYNQ-7000의 것을 사용하였고,
Tool은 Vivado를 사용하였습니다.
Nucleo 보드는 CUBE IDE에서 제어하였습니다.
키보드를 통해 입력한 문자들이
Nucleo 보드를 거쳐 LCD에 잘 출력되는 것을 볼 수 있습니다.
이번에는 합성 및 P&R 단계를 생략하고 테스트를 먼저 진행하였습니다.
이후 시간이 된다면 해당 부분도 포스팅하도록 하겠습니다.
'RTL, Synthesis, P&R' 카테고리의 다른 글
Cortex-M0 SOC 활용 - (1) AMBA3 AHB-Lite와 Pheripheral (0) | 2024.09.09 |
---|---|
16KB Cache Memory Controller - (1) RTL 및 Coverage (0) | 2024.09.07 |
UART 통신 - (1) RTL (0) | 2024.09.03 |
32-bit SPI Interface - (3) Arduino로 FPGA Motor 제어 (0) | 2024.09.03 |
32-bit SPI Interface - (2) Synthesis, P&R (0) | 2024.09.03 |