[Embedded System Lab] Chapter 3 - FSM & BRAM
"FSM & BRAM"
[Objective]
coe 작성을 통해 Text LCD 화면 출력을 확인하며 BRAM Logic 이해하기
[Theory]
▣ FSM(Finite State Machine)
· 유한 개의 상태(State)와 상태 전이 규칙(Transition Rules)으로 어떤 시스템의 동작을 정의하는 Model
→ {대기 상태, 동작 상태, 완료 상태}
· FSM은 State Diagram 또는 State Transition Table로 표현 가능하며 Mealy Machine과 Moore Machine으로 분리
▶ State Diagram
· FSM의 각 상태 및 입력에 따른 상태 전이와 출력을 나타내는 그림
** 상태, 상태 전이, 입력, 출력을 포함
> Mealy Machine
· 출력이 현재 상태 및 입력에 의해 결정되는 FSM
> Moore Machine
· 출력이 현재 상태에 의해서만 결정되는 FSM
▶ State Transition Table
· FSM을 표로 표현하는 방법
> Mealy Machine
· 출력이 현재 상태 및 입력에 의해 결정
> Moore Machine
· 출력이 현재 상태에 의해서만 결정
** Moore Machine
· Moore Machine의 출력 : 출력은 현재 상태에 의해서만 결정
▣ BRAM(Block-RAM)
· 데이터를 저장할 수 있는 FPGA 내의 특정 공간
· BRAM의 총 용량은 BRAM의 Width 및 Depth에 의해 결정 (Width = 각 데이터의 크기 / Depth = 데이터의 총 개수)
> Single-Port RAM
· 1개의 Port → 한 번에 한 작업만 가능(Read 또는 Write 중 하나)
· 한 Clock에 하나의 주소만 접근 가능
> Simple Dual-Port RAM
· 2개의 Port가 각각 역할 존재 → A Port : Read 전용 / B Port : Write 전용 (반대 가능)
· Read와 Write를 동시에 가능하지만, 각 Port의 역할 고정
> True Dual-Port RAM
· 2개의 Port 모두 자유롭게 사용 가능 → A, B Port 모두 Read/Write 가능
· A, B Port 각각 독립적으로 동작
▶ True Dual-Port RAM
∴ 동시에 2개의 주소에 접근 가능 (단, 같은 주소 접근 시 주의 필요)
→ Ex. A Port : CPU처럼 사용, B Port : DMA처럼 사용 / A Port : 주소 0 읽기, B Port : 주소 100 쓰기
· CLK(Clock) : Port에서 사용할 Clock 신호
· EN(Enable) : Port의 활성화 여부 (0 : Port 비활성화 / 1 : Port 활성화)
· WE(Write Enable) : Port의 Read/Write 모드 여부 (0 : Read Mode / 1 : Write Mode)
· ADDR(Address) : Port로 접근할 주소 (Read Mode : 데이터를 가져올 주소 / Write Mode : 데이터를 저장할 주소)
· DIN(Data In) : Port에 입력되는 데이터 (Write Mode일 때 데이터를 입력하기 위해 사용)
· DOUT(Data Out) : Port에서 출력되는 데이터 (Read Mode일 때 데이터를 출력하기 위해 사용)
▶ Initialize BRAM
· COE(Coefficient) 파일을 통해 BRAM의 초기 데이터를 임의로 설정 가능
→ 별도로 BRAM을 초기화하지 않을 경우 데이터의 기본값은 0
> COE 파일
· BRAM을 초기화할 데이터가 저장되어 있는 Text 파일 (*.coe)
· 데이터를 표현할 Radix를 정의 후, 각 줄에 데이터를 하나씩 입력
· BRAM에 Text 저장 → LCD에 출력
· radix = 16 : 16진수
· memory[0] : 6d7d
· memory[1] : 6d9d
· memory[1] : 6d9d
· memory[2] : 659c
· memory[3] : 659d
▣ Text-LCD
· RPS-Z7020-TK Board에 Text-LCD Panel과 Controller가 모두 장착되어 있음
→ Controller에 올바른 제어 신호를 전송하기만 하면 Text-LCD에 원하는 Text 출력 가능
· RS(Register Select) : 명령어를 입출력할 것인지, 데이터를 입출력할 것인지 선택 (0 : 명령어 / 1 : 데이터)
· RW(Read/Write) : Read/Write 선택 (0 : Write / 1 : Read)
· E(Enable) : Enable 신호 (0 : 비활성화 / 1 : 활성화)
· DB7~DB0 : 8-bit 데이터 Bus → 명령어나 문자 Code가 이 Bus를 통해 전달
· Instruction Register : LCD 동작 명령 저장
· Data Register : 표시할 문제 데이터 저장
→ RS 값에 따라 같은 DB Bus로 들어오는 8-bit 값이 명령어로 해석되거나 문자 데이터로 해석됨
▶ Text-LCD의 구성 요소
· CGROM(Character Generator ROM) : 기본 문자 패턴이 저장된 ROM
· CGRAM(Character Generator RAM) : 임의의 문자 패턴을 저장하는 RAM
· DDRAM(Display Data RAM) : LCD에 표시할 패턴의 Index를 저장하는 RAM
> Text-LCD의 동작 원리
· 사용자가 DDRAM의 각 주소에 8-bit의 패턴 Index를 저장하면, LCD의 각 위치에 사용자가 지정한 패턴이 출력됨
Ex. DDRAM의 "00" 주소에 "0101_0000" 저장 → LCD 좌측 상단에 "P" 출력
▶ Controlling Text-LCD
· RS(Register Select) : 명령어를 입출력할 것인지, 데이터를 입출력할 것인지 선택 (0 : 명령어 / 1 : 데이터)
· RW(Read/Write) : Read/Write 선택 (0 : Write / 1 : Read)
· E(Enable) : Enable 신호 (0 : 비활성화 / 1 : 활성화)
· DB7~DB0 : 8-bit 데이터 Bus → 명령어나 문자 Code가 이 Bus를 통해 전달
· S : 문자를 입력할 때 화면 자체를 함께 Shift할지 결정
· D : Display On/Off 선택
· C : Cursor On/Off 선택
· B : Cursor 깜박임 On/Off 선택
· S/C : Display/Cursor Shift 선택 (0 : Cursor Shift / 1 : Display Shift)
· R/L : 좌우 선택 (0 : 왼쪽 / 1 : 오른쪽)
· IF : 데이터 Bus 폭 설정 (0 : 4-bit Mode 사용 / 1 : 8-bit Mode 사용) → DB7~DB0를 사용하므로 보통 8-bit Mode
· N : LCD의 행 설정 (0 : 1행 LCD / 1 : 2행 LCD) → RPS-Z7020-TK Board는 2줄 LCD이므로 일반적으로 N = 1
· F : 문자 Font 크기/패턴 형태 설정 (0 : 5 x 7 패턴 사용 / 1 : 4 x 10 패턴 사용)
· CGRAM 주소 설정 : 사용자 정의 문자를 저장할 CGRAM 위치를 지정
· DDRAM 주소 설정 : 화면의 어느 위치에 문자를 표시할지 정하는 DDRAM 주소를 지정
· Busy Flag : 1인 경우 LCD가 아직 이전 명령 처리 중
· CGRAM/DDRAM에 데이터 쓰기 : RS = 1, RW = 0 → 데이터 Write
· CGRAM/DDRAM에서 데이터 읽기 : RS = 1, RW = 1 → 데이터 Read
▶ Text-LCD의 Timing Diagram (Write 동작)
① RS, RW 설정 : RS = 0/1 → 명령어/데이터 선택, RW = 0 → Write
② DB[7:0] 데이터 설정 : DB = 보낼 명령 또는 문자 Code (데이터는 미리 안정되어 있어야 함)
② DB[7:0] 데이터 설정 : DB = 보낼 명령 또는 문자 Code (데이터는 미리 안정되어 있어야 함)
③ Enable 신호 : E = 1 → "지금 데이터를 읽겠다"는 상태 (tDSW : 안정된 데이터 유지)
④ Enable 신호 : E = 0 → 이 순간 데이터가 실제로 LCD에 저장됨 (tH : 데이터 유지)
** E Falling Edge에서 LCD가 해당 데이터를 Sampling
※ tDSW : Data Setup Time / tH : Hold Time
[Experiment]
▣ Setting
> top.xdc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | set_property IOSTANDARD "LVCMOS33" [get_ports "lcdclk"] set_property PACKAGE_PIN "M19" [get_ports "lcdclk"] set_property IOSTANDARD "LVCMOS33" [get_ports "resetn"] set_property PACKAGE_PIN "Y18" [get_ports "resetn"] set_property IOSTANDARD "LVCMOS33" [get_ports "lcd_rs"] set_property PACKAGE_PIN "F19" [get_ports "lcd_rs"] set_property IOSTANDARD "LVCMOS33" [get_ports "lcd_rw"] set_property PACKAGE_PIN "H20" [get_ports "lcd_rw"] set_property IOSTANDARD "LVCMOS33" [get_ports "lcd_en"] set_property PACKAGE_PIN "G19" [get_ports "lcd_en"] set_property IOSTANDARD "LVCMOS33" [get_ports "lcd_data[*]"] set_property PACKAGE_PIN "A22" [get_ports "lcd_data[7]"] set_property PACKAGE_PIN "D22" [get_ports "lcd_data[6]"] set_property PACKAGE_PIN "C22" [get_ports "lcd_data[5]"] set_property PACKAGE_PIN "E21" [get_ports "lcd_data[4]"] set_property PACKAGE_PIN "D21" [get_ports "lcd_data[3]"] set_property PACKAGE_PIN "B21" [get_ports "lcd_data[2]"] set_property PACKAGE_PIN "B22" [get_ports "lcd_data[1]"] set_property PACKAGE_PIN "H19" [get_ports "lcd_data[0]"] | cs |
> textlcd.v
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | `timescale 1ns / 1ps `define lcd_blank 8'b00100000 `define lcd_dash 8'b00101101 `define lcd_colon 8'b00111010 `define lcd_comma 8'b00101100 `define lcd_Dot 8'b00101110 `define lcd_0 8'b00110000 `define lcd_1 8'b00110001 `define lcd_2 8'b00110010 `define lcd_3 8'b00110011 `define lcd_4 8'b00110100 `define lcd_5 8'b00110101 `define lcd_6 8'b00110110 `define lcd_7 8'b00110111 `define lcd_8 8'b00111000 `define lcd_9 8'b00111001 `define lcd_a 8'b01000001 `define lcd_b 8'b01000010 `define lcd_c 8'b01000011 `define lcd_d 8'b01000100 `define lcd_e 8'b01000101 `define lcd_f 8'b01000110 `define lcd_g 8'b01000111 `define lcd_h 8'b01001000 `define lcd_i 8'b01001001 `define lcd_j 8'b01001010 `define lcd_k 8'b01001011 `define lcd_l 8'b01001100 `define lcd_m 8'b01001101 `define lcd_n 8'b01001110 `define lcd_o 8'b01001111 `define lcd_p 8'b01010000 `define lcd_q 8'b01010001 `define lcd_r 8'b01010010 `define lcd_s 8'b01010011 `define lcd_t 8'b01010100 `define lcd_u 8'b01010101 `define lcd_v 8'b01010110 `define lcd_w 8'b01010111 `define lcd_x 8'b01011000 `define lcd_y 8'b01011001 `define lcd_z 8'b01011010 `define lcd_under 8'b01011111 `define lcd_s_a 8'b01100001 `define lcd_s_b 8'b01100010 `define lcd_s_c 8'b01100011 `define lcd_s_d 8'b01100100 `define lcd_s_e 8'b01100101 `define lcd_s_f 8'b01100110 `define lcd_s_g 8'b01100111 `define lcd_s_h 8'b01101000 `define lcd_s_i 8'b01101001 `define lcd_s_j 8'b01101010 `define lcd_s_k 8'b01101011 `define lcd_s_l 8'b01101100 `define lcd_s_m 8'b01101101 `define lcd_s_n 8'b01101110 `define lcd_s_o 8'b01101111 `define lcd_s_p 8'b01110000 `define lcd_s_q 8'b01110001 `define lcd_s_r 8'b01110010 `define lcd_s_s 8'b01110011 `define lcd_s_t 8'b01110100 `define lcd_s_u 8'b01110101 `define lcd_s_v 8'b01110110 `define lcd_s_w 8'b01110111 `define lcd_s_x 8'b01111000 `define lcd_s_y 8'b01111001 `define lcd_s_z 8'b01111010 `define add_line_1 8'b10000000 `define add_line_2 8'b11000000 `define add_line_3 8'b10010100 `define add_line_4 8'b11010100 module textlcd( input lcdclk, // Clock 신호 input resetn, // Reset 신호 input [31:0] bram_read_data_p0, input [31:0] bram_read_data_p1, output lcd_rs, // 명령어, 데이터 구분 output lcd_rw, // Read/Write 신호 output reg lcd_en, // Enable 신호 output reg [12:0] bram_read_addr_p0, output reg [12:0] bram_read_addr_p1, output [7:0] lcd_data // LCD 출력 데이터 ); wire [31:0] reg_a; wire [31:0] reg_b; wire [31:0] reg_c; wire [31:0] reg_d; wire [31:0] reg_e; wire [31:0] reg_f; wire [31:0] reg_g; wire [31:0] reg_h; reg [23:0] delay_lcdclk; //clock reg [15:0] count_lcd; // lcd_en is declared as an output reg above reg [4:0] lcd_mode = 0; wire [4:0] mode_pwron = 1 ; // power on wire [4:0] mode_fnset = 2 ; // function set wire [4:0] mode_onoff = 3 ; // display on/off Control wire [4:0] mode_entr1 = 4 ; // wire [4:0] mode_entr2 = 5 ; // wire [4:0] mode_entr3 = 6 ; // wire [4:0] mode_seta1 = 7 ; // set addr 1st line wire [4:0] mode_wr1st = 8 ; // Write 1st line wire [4:0] mode_seta2 = 9 ; // set addr 2nd line wire [4:0] mode_wr2nd = 10; // Write 2nd line wire [4:0] mode_delay = 11; // dealy wire [4:0] mode_actcm = 12; // user command reg [9:0] set_data; // Data read from the BRAM reg [31:0] local_data[0:7]; // NOTE: For a 32-bit-wide ROM, each read returns 4 ASCII characters. // If your COE packs bytes as 32'h596F6E73 for "Yons", then the first character // should be taken from [31:24], matching the write logic below. assign reg_a = local_data[0]; assign reg_b = local_data[1]; assign reg_c = local_data[2]; assign reg_d = local_data[3]; assign reg_e = local_data[4]; assign reg_f = local_data[5]; assign reg_g = local_data[6]; assign reg_h = local_data[7]; // ------------------------------------------------------------ // BRAM addressing for 32-bit-wide ROM // - One address returns 32 bits = 4 ASCII characters. // - This design reads 4 words for line 1 and 4 words for line 2. // - If you want the 0th word to be "empty", keep LINE1_BASE=1. // (i.e., put blanks at word address 0 in your COE). // ------------------------------------------------------------ localparam integer LINE_CHARS = 16; localparam integer WORD_BYTES = 4; localparam integer LINE_WORDS = LINE_CHARS / WORD_BYTES; // 4 localparam [12:0] LINE1_BASE = 13'd1; localparam [12:0] LINE2_BASE = LINE1_BASE + LINE_WORDS; // 5 // Read 8 words total (4 per port) into local_data[] once during init. // We sample the BRAM data a few cycles after entering each init state // to avoid any ambiguity about BRAM read latency. always @ (posedge lcdclk) begin if (resetn == 0) begin bram_read_addr_p0 <= LINE1_BASE; bram_read_addr_p1 <= LINE2_BASE; local_data[0] <= 32'b0; local_data[1] <= 32'b0; local_data[2] <= 32'b0; local_data[3] <= 32'b0; local_data[4] <= 32'b0; local_data[5] <= 32'b0; local_data[6] <= 32'b0; local_data[7] <= 32'b0; end else begin // Ensure base addresses are applied during the initial power-on state if (lcd_mode == mode_pwron && delay_lcdclk == 24'd0) begin bram_read_addr_p0 <= LINE1_BASE; bram_read_addr_p1 <= LINE2_BASE; end // Line 1 word 0 if (lcd_mode == mode_fnset) begin if (delay_lcdclk == 24'd10) begin local_data[0] <= bram_read_data_p0; local_data[4] <= bram_read_data_p1; end if (delay_lcdclk == 24'd11) begin bram_read_addr_p0 <= bram_read_addr_p0 + 13'd1; bram_read_addr_p1 <= bram_read_addr_p1 + 13'd1; end end // Line 1 word 1 if (lcd_mode == mode_onoff) begin if (delay_lcdclk == 24'd10) begin local_data[1] <= bram_read_data_p0; local_data[5] <= bram_read_data_p1; end if (delay_lcdclk == 24'd11) begin bram_read_addr_p0 <= bram_read_addr_p0 + 13'd1; bram_read_addr_p1 <= bram_read_addr_p1 + 13'd1; end end // Line 1 word 2 if (lcd_mode == mode_entr1) begin if (delay_lcdclk == 24'd10) begin local_data[2] <= bram_read_data_p0; local_data[6] <= bram_read_data_p1; end if (delay_lcdclk == 24'd11) begin bram_read_addr_p0 <= bram_read_addr_p0 + 13'd1; bram_read_addr_p1 <= bram_read_addr_p1 + 13'd1; end end // Line 1 word 3 if (lcd_mode == mode_entr2) begin if (delay_lcdclk == 24'd10) begin local_data[3] <= bram_read_data_p0; local_data[7] <= bram_read_data_p1; end // No need to increment further after the last read for each line. end end end // Counterx2000 & //Counterx40 always @ (posedge lcdclk) begin if (resetn == 0) begin delay_lcdclk <= 0; count_lcd <= 0; lcd_en <= 1'b0; end else begin // Counterx2000 if (delay_lcdclk < 1999) delay_lcdclk <= delay_lcdclk + 1; else delay_lcdclk <= 0; // Counterx40 if (delay_lcdclk == 0) begin if (count_lcd < 40) count_lcd <= count_lcd + 1; else count_lcd <= 6; end if (delay_lcdclk == 200) lcd_en <= 1'b1; else if (delay_lcdclk == 1800) lcd_en <= 1'b0; end end // decoder switch always @ (posedge lcdclk) begin if (resetn == 0) lcd_mode <= mode_pwron; else begin case (count_lcd) 0 : lcd_mode <= mode_pwron ; 1 : lcd_mode <= mode_fnset ; 2 : lcd_mode <= mode_onoff ; 3 : lcd_mode <= mode_entr1 ; 4 : lcd_mode <= mode_entr2 ; 5 : lcd_mode <= mode_entr3 ; 6 : lcd_mode <= mode_seta1 ; 7 : lcd_mode <= mode_wr1st ; 23 : lcd_mode <= mode_seta2 ; 24 : lcd_mode <= mode_wr2nd ; 40 : lcd_mode <= mode_delay ; 41 : lcd_mode <= mode_actcm ; default : begin end endcase end end assign lcd_rs = set_data[9]; assign lcd_rw = set_data[8]; assign lcd_data = set_data[7:0]; // decoder output (combinational): drives lcd_rs/lcd_rw/lcd_data via set_data // IMPORTANT: Do NOT increment BRAM addresses or latch BRAM data here. // Those are done synchronously in the BRAM reader block above. always @ (*) begin if (resetn == 0) begin set_data = 10'b0000000000; end else begin // Default: harmless command (Return Home). Also avoids latch inference. set_data = {2'b00, 8'h02}; case (lcd_mode) mode_pwron : set_data = {2'b00, 8'h38}; mode_fnset : set_data = {2'b00, 8'h38}; mode_onoff : set_data = {2'b00, 8'h0e}; mode_entr1 : set_data = {2'b00, 8'h06}; mode_entr2 : set_data = {2'b00, 8'h02}; mode_entr3 : set_data = {2'b00, 8'h01}; mode_seta1 : set_data = {2'b00, 8'h80}; mode_wr1st : begin // default character: space set_data = {1'b1, 1'b0, 8'h20}; case (count_lcd) 7 : set_data = {1'b1, 1'b0, reg_a[31:24]}; 8 : set_data = {1'b1, 1'b0, reg_a[23:16]}; 9 : set_data = {1'b1, 1'b0, reg_a[15: 8]}; 10 : set_data = {1'b1, 1'b0, reg_a[7 : 0]}; 11 : set_data = {1'b1, 1'b0, reg_b[31:24]}; 12 : set_data = {1'b1, 1'b0, reg_b[23:16]}; 13 : set_data = {1'b1, 1'b0, reg_b[15: 8]}; 14 : set_data = {1'b1, 1'b0, reg_b[7 : 0]}; 15 : set_data = {1'b1, 1'b0, reg_c[31:24]}; 16 : set_data = {1'b1, 1'b0, reg_c[23:16]}; 17 : set_data = {1'b1, 1'b0, reg_c[15: 8]}; 18 : set_data = {1'b1, 1'b0, reg_c[7 : 0]}; 19 : set_data = {1'b1, 1'b0, reg_d[31:24]}; 20 : set_data = {1'b1, 1'b0, reg_d[23:16]}; 21 : set_data = {1'b1, 1'b0, reg_d[15: 8]}; 22 : set_data = {1'b1, 1'b0, reg_d[7 : 0]}; default: begin end endcase end mode_seta2 : set_data = {2'b00, 8'hc0}; mode_wr2nd : begin set_data = {1'b1, 1'b0, 8'h20}; case (count_lcd) 24 : set_data = {1'b1, 1'b0, reg_e[31:24]}; 25 : set_data = {1'b1, 1'b0, reg_e[23:16]}; 26 : set_data = {1'b1, 1'b0, reg_e[15: 8]}; 27 : set_data = {1'b1, 1'b0, reg_e[7 : 0]}; 28 : set_data = {1'b1, 1'b0, reg_f[31:24]}; 29 : set_data = {1'b1, 1'b0, reg_f[23:16]}; 30 : set_data = {1'b1, 1'b0, reg_f[15: 8]}; 31 : set_data = {1'b1, 1'b0, reg_f[7 : 0]}; 32 : set_data = {1'b1, 1'b0, reg_g[31:24]}; 33 : set_data = {1'b1, 1'b0, reg_g[23:16]}; 34 : set_data = {1'b1, 1'b0, reg_g[15: 8]}; 35 : set_data = {1'b1, 1'b0, reg_g[7 : 0]}; 36 : set_data = {1'b1, 1'b0, reg_h[31:24]}; 37 : set_data = {1'b1, 1'b0, reg_h[23:16]}; 38 : set_data = {1'b1, 1'b0, reg_h[15: 8]}; 39 : set_data = {1'b1, 1'b0, reg_h[7 : 0]}; default: begin end endcase end mode_delay : set_data = {2'b00, 8'h02}; mode_actcm : set_data = {2'b00, 8'h02}; default : begin end endcase end end endmodule | cs |
① 우리가 사용하는 Board인 xc7z020clg484-1을 Parts로 추가한 후, 제공된 textlcd.v 파일과 top.xdc 파일을 각각 Design Sources와 Constraints에 추가
② Create Block Design을 실행한 후, Block Memory Generator를 실행
③ 생성된 Module을 Double Click하여 다음과 같이 설정
· Basic : Mode = Stand Alone, Memory Type = Dual Port ROM
· Port A : Width = 12, Depth = 8192, Enable Port Type = Always Enabled
· Port B : Width = 12, Depth = 8192, Enable Port Type = Always Enabled
· Other Options : Load Init File = Check, Coe File = Edit
· ../bd/design_1 위치에 “my_coe” 이름으로 저장
· coe 파일 초기화
④ Diagram에서 Symbol의 “+”를 Click하여 모든 Port가 보이도록 설정
⑤ Sources에서 textlcd.v 파일을 Diagram으로 Drag하여 Block을 추가
⑥ {resetn, lcdclk, lcd_rs, lcd_rw, lcd_en, lcd_data[7:0]}을 선택하고 우Click한 후, Make External을 선택
⑦ 생성된 External Ports를 top.xdc 파일에 설정된 Port 이름과 Matching 시킴
⑧ 위 Block Diagram 그림과 같이 Port를 연결한 후, Validate Design을 통해 유효성을 확인
⑨ design_1을 우Click해서 Create HDL Wrapper를 실행시킨 후, “Let Vivado manage wrapper and auto-update”와 “Generated wrapper will be used as Top Module”을 Click 후 실행
⑩ Bitstream을 생성한 후, Board를 연결하여 결과를 확인
▣ Execution
1) 3031_3233_3435_3637_3839 → 0123456789
· coe 파일의 memory_initialization_radix는 16진수로 표현하기 위해 16으로 설정
· “0123456789”를 Text LCD 화면에 출력을 목표로 설정
· memory_initialization_vector를 “3031_3233_3435_3637_3839”로 설정
· Text LCD 화면 출력 결과는 위 그림과 같이 앞의 2개의 숫자 01이 출력되지 않고, 2개의 숫자 출력 후 ||가 출력되는 것을 확인 가능
2) 3839_3031_3637_3233_3435_3435_3433_3637_3031_3839 → 89016723454543670189
· 출력 개수를 늘려 “89016723454543670189”를 Text LCD 화면에 출력을 목표로 설정
· memory_initialization_vecotr를 “3839_3031_3637_3233_3435_3435_3433_3637_3031_3839”로 설정
· Text LCD 화면 출력 결과는 위 그림과 같이 앞의 2개의 숫자 89가 출력되지 않고, 2개의 숫자 출력 후 ||가 출력되는 것을 확인 가능
3) 38393031_36373233_34353435_34333637_30313839 → 89016723454543670189
· Execution 2와 같이 “89016723454543670189”를 Text LCD 화면에 출력을 목표로 설정
· memory_initialization_vecotr를 "38393031_36373233_34353435_34333637_30313839"로 8-bit 묶음으로 설정
· Text LCD 화면 출력 결과는 위 그림과 같이 2개 숫자 이후 ||가 출력되는 것 수정 완료
→ But, 여전히 앞의 4개의 숫자 8901이 출력되지 않고, 2번째 줄은 ||가 출력되는 것을 확인 가능
4) 00000000_30313233_34353637_38393031_32333435-00000000_36373839_30313233_34353637_38393031
→ 0123456789012345-6789012345678901
· "0123456789012345-6789012345678901"을 Text LCD 화면에 출력을 목표로 설정
· memory_initialization_vector를 앞에 "00000000"을 추가하여 처음 4개의 숫자가 출력되지 않는 것을 보완
→ “00000000_30313233_34353637_38393031_32333435-00000000_36373839_30313233_34353637_38393031”
· Text LCD 화면 출력 결과는 위 그림과 같이 처음 4개의 숫자가 잘 출력되고 의도한대로 1번째 줄이 출력 완료
→ But, 2번째 줄 앞의 4개의 숫자가 ||로 출력되는 것을 확인 가능
5) 00000000_30313233_34353637_38393031_32333435-36373839_30313233_34353637_38393031
→ 0123456789012345-6789012345678901
· Execution 4와 같이 "0123456789012345-6789012345678901"을 Text LCD 화면에 출력을 목표로 설정
· memory_initialization_vector를 2번째 줄은 앞에 "00000000"을 제거하여 처음 4개의 글자가 ||로 출력되는 것을 보완
→ "00000000_30313233_34353637_38393031_32333435-36373839_30313233_34353637_38393031"
· Text LCD 화면 출력 결과는 위 그림과 같이 의도한대로 숫자가 잘 출력되는 것을 확인 가능
6) 20202020_48656F20_????????_????2020_20202020-32303232_????????_????2020_20202020
→ 이름 - 학번
· "이름 - 학번"을 Text LCD 화면에 출력을 목표로 설정
· memory_initialization_vector를 1번째 줄 앞의 4글자가 출력되도록 빈칸을 의미하는 “20202020”으로 변경
→ "20202020_48656F20_????????_????2020_20202020-32303232_????????_????2020_20202020"
· Text LCD 화면 출력 결과는 위 그림과 같이 이름과 학번이 제대로 출력되는 것을 확인 가능
[Discussion]
· ||는 아무것도 지정하지 않거나 00 ~ 0F를 지정지정하면 출력
→ Execution 6의 1번째 줄 앞의 4글자는 ||를 나타내는 "00000000"이 아닌 빈 칸을 의미하는 "20202020"으로 변경
→ But, ||를 의미하는 "00000000"을 넣어도 처음 8-bit 다음부터 화면에 출력되기 때문에 상관 X
→ 1번째 줄 맨 앞의 8-bit가 화면에 출력
∴ localparam[12:0] LINE1_BASE를 13'd0으로 변경 후 memory_initialization_vector를 아래와 같이 설정
· 48656F20_????????_????2020_20202020-32303232_????????_????2020_20202020
→ Execution 6과 동일한 결과 출력
[Thinking]
Q) BRAM에서 읽는 데이터의 폭은 얼마인가?
A) 32-bit
∵ textlcd.v 파일에 bram_read_data가 [31:0]로 선언되어 있으므로, BRAM에서 읽는 데이터의 폭은 32-bit
Q) 1 Word Byte가 몇 글자를 의미하는가?
A) 4글자
∵ 1 Word Byte = 4-Byte = 32-bit이며, textlcd.v 파일에 {[31:24], [23:16], [15:8], [7:0]}로 32-bit를 8-bit 문자 4개로 나누어 사용
Q) LCD 한 줄에 몇 글자인가?
A) 16글자
∵ textlcd.v 파일에 localparam integer LINE_CHARS = 16으로 선언되어 한 줄에 16글자가 표시
→ 4글자는 1 Word Byte이므로 한 줄에 4개의 Word Byte를 Read
Q) 1번째 줄의 첫 Word는 몇 번 주소부터 시작하는가?
A) 주소 1
∵ textlcd.v 파일에 localparam [12:0] LINE1_BASE = 13'd1로 선언되어 첫 Word는 1번 주소부터 시작
[Reference]
· Verilog_Basics_3 (Embedded System Lab : Chapter3) - William J. Song
· Step-by-Step (Embedded System Lab : Chapter3) - William J. Song
댓글
댓글 쓰기