特別版No.3のmax10生基板を活用する.
●プロンプト
システムクロックの指示を忘れたので,後から追加で指示した.
FPGAデザインを設計して,Quartus Primer Lite用のプロジェクトを作る
# アプリケーション
2色マトリクスLEDのダイナミック点灯制御
## マトリクスLEDの信号
8ビットシフトレジスタ経由のColumn Green
8ビットシフトレジスタ経由のRow
8ビットシフトレジスタ経由のColumn Red
3つの信号のクロックは1つ(信号名:CLOCK)
## 表示内容
1秒ごとに異なる文字を表示する
表示するのは数字の0~9まで
# HDL
SystemVerilog
# 合成系
Quartus Prime lite
# デバイス
## FPGAチップ
max10
## LEDマトリクス
LTP-12188M-08 (https://akizukidenshi.com/goodsaffix/ltp12188m.pdf)
## シフトレジスタ
74HC595(https://toshiba.semicon-storage.com/info/TC74HC595AP_datasheet_ja_20140301.pdf?did=16501&prodName=TC74HC595AP)
## ベースボード
https://github.com/Lathe-Mariel/PCB_CQmax10
# 物理制約
信号は全て,3.3-V LVCMOS
信号名 | FPGA ピン | 内容
---|---|---
ROW | 98 | Row(Anode)
COL_GREEN | 101 | Column Green(Cathode Green)
COL_RED | 99 Column Red(Cathode Red)
CLR1 | 100 | Clear1(column green shift regster clear, active low)
CLR3 | 96 | Clear3(row shift regster clear, active low)
CLR2 | 105 | Clear2(column red shift regster clear, active low)
RCLOCK | 97 | R Clock(shift regster rck)
CLOCK | 106 | Clock(shift regster clock)
clk | 88 | System clock
rst_n | 17 | system reset_n広げる
●生成されたRTLコード
マトリクスLEDのLTP-12188M-08(Lite-On Technology Corporation)はRow側がアノードで,Column(赤・緑)側がカソードである.プロンプトで指示してあったが,Claudeはいつも通り,定数で入れ替えできるようなRTLコードを生成した.
●トップモジュール
実機で動かしたところ数字が鏡像だったので,手動でコード修正し,行の表示順を入れ替えた.
`default_nettype none
// コメント省略
module top_matrix_led #(
parameter int CLK_FREQ_HZ = 50_000_000, // system clk = 50 MHz
parameter int SHIFT_HALF_PERIOD = 10, // sys-clk cycles per CLOCK/RCLOCK half-period
parameter int ROW_HOLD_TICKS = CLK_FREQ_HZ / 1000, // ~1 kHz row switch -> ~125 Hz frame refresh
parameter int DIGIT_HOLD_TICKS = CLK_FREQ_HZ, // 1 second per digit
parameter int CLR_PULSE_TICKS = 16, // width of the power-up /MR pulse
parameter bit ROW_ACTIVE_HIGH = 1'b1, // see assumption (2) above
parameter bit COL_ACTIVE_LOW = 1'b1 // see assumption (2) above
)(
input logic clk, // pin 88 : system clock
input logic rst_n, // pin 17 : asynchronous reset, active low
output logic ROW, // pin 98 : row shift register serial data in
output logic COL_GREEN, // pin 101 : column-green shift register serial data in
output logic COL_RED, // pin 99 : column-red shift register serial data in
output logic CLR1, // pin 100 : column-green shift reg clear, active low
output logic CLR3, // pin 96 : row shift reg clear, active low
output logic CLR2, // pin 105 : column-red shift reg clear, active low
output logic RCLOCK, // pin 97 : storage register clock (latch, shared)
output logic CLOCK // pin 106 : shift register clock (shared)
);
// ---------------------------------------------------
// Reset synchronizer (async assert, sync de-assert)
// ---------------------------------------------------
logic [1:0] rst_sync;
logic rst_int_n;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) rst_sync <= 2'b00;
else rst_sync <= {rst_sync[0], 1'b1};
end
assign rst_int_n = rst_sync[1];
// --------------------------------------------------
// 1-second digit counter: cycles 0 -> 9 -> 0 ...
// --------------------------------------------------
logic [25:0] sec_cnt;
logic [3:0] digit_idx;
always_ff @(posedge clk or negedge rst_int_n) begin
if (!rst_int_n) begin
sec_cnt <= 26'd0;
digit_idx <= 4'd0;
end else if (sec_cnt == DIGIT_HOLD_TICKS-1) begin
sec_cnt <= 26'd0;
digit_idx <= (digit_idx == 4'd9) ? 4'd0 : digit_idx + 4'd1;
led <= !led;
end else begin
sec_cnt <= sec_cnt + 26'd1;
end
end
// -------------------------------------------------
// Font lookup for the row currently being scanned
// -------------------------------------------------
logic [2:0] row_idx; // 0-7, which glyph row is being scanned right now
logic [7:0] col_pattern; // 1 = pixel lit, bit7 = leftmost column
font_rom u_font (
.digit (digit_idx),
.row (row_idx),
.pattern (col_pattern)
);
// Even digit -> green, odd digit -> red (exercises both color channels)
logic [7:0] row_sel_raw, colg_raw, colr_raw;
always_comb begin
row_sel_raw = 8'h80 >> row_idx; // Row順反転のため手動で編集 // one-hot: selects active row
colg_raw = digit_idx[0] ? 8'h00 : col_pattern;
colr_raw = digit_idx[0] ? col_pattern : 8'h00;
end
// Apply hardware polarity (see assumption 2)
logic [7:0] row_pat, colg_pat, colr_pat;
assign row_pat = ROW_ACTIVE_HIGH ? row_sel_raw : ~row_sel_raw;
assign colg_pat = COL_ACTIVE_LOW ? ~colg_raw : colg_raw;
assign colr_pat = COL_ACTIVE_LOW ? ~colr_raw : colr_raw;
// -----------------------------------------------
// Scan / shift-out sequencer
// -----------------------------------------------
typedef enum logic [3:0] {
ST_CLR, // hold CLR1/CLR2/CLR3 low once after reset
ST_LOAD, // load row/col pattern for current row into shift regs
ST_BIT_SETUP, // drive data bit on ROW/COL_GREEN/COL_RED, CLOCK low
ST_BIT_CLKHI, // CLOCK high (rising edge shifts the bit into all 3 595's)
ST_LATCH_SETUP, // RCLOCK low, settle
ST_LATCH_HI, // RCLOCK high (rising edge latches all 3 storage regs)
ST_LATCH_LO, // RCLOCK back low
ST_HOLD // display current row for ROW_HOLD_TICKS, then next row
} state_e;
state_e state;
logic [7:0] row_shift, colg_shift, colr_shift; // MSB-first shift-out registers
logic [2:0] bit_idx; // counts which of the 8 bits is being sent
logic [25:0] timer;
always_ff @(posedge clk or negedge rst_int_n) begin
if (!rst_int_n) begin
state <= ST_CLR;
row_idx <= 3'd0;
bit_idx <= 3'd0;
timer <= 26'd0;
row_shift <= 8'h00;
colg_shift <= 8'h00;
colr_shift <= 8'h00;
ROW <= 1'b0;
COL_GREEN <= 1'b0;
COL_RED <= 1'b0;
CLOCK <= 1'b0;
RCLOCK <= 1'b0;
CLR1 <= 1'b0; // asserted (active-low) immediately at reset
CLR2 <= 1'b0;
CLR3 <= 1'b0;
end else begin
unique case (state)
// ---- power-up clear pulse ----
ST_CLR: begin
CLR1 <= 1'b1;
CLR2 <= 1'b1;
CLR3 <= 1'b1;
CLOCK <= 1'b0;
RCLOCK <= 1'b0;
if (timer == CLR_PULSE_TICKS-1) begin
timer <= 26'd0;
CLR1 <= 1'b1; // release clear (idle = high)
CLR2 <= 1'b1;
CLR3 <= 1'b1;
state <= ST_LOAD;
end else begin
timer <= timer + 26'd1;
end
end
// -- load next row's pattern into the shift-out regs --
ST_LOAD: begin
if(timer == SHIFT_HALF_PERIOD-1)begin
row_shift <= row_pat;
colg_shift <= colg_pat;
colr_shift <= colr_pat;
bit_idx <= 3'd0;
timer <= 26'd0;
CLOCK <= 1'b0;
state <= ST_BIT_SETUP;
end else begin
timer <= timer + 26'd1;
end
end
// -- present MSB of each shift-out reg, CLOCK low --
ST_BIT_SETUP: begin
ROW <= row_shift[7];
COL_GREEN <= colg_shift[7];
COL_RED <= colr_shift[7];
CLOCK <= 1'b0;
if (timer == SHIFT_HALF_PERIOD-1) begin
timer <= 26'd0;
state <= ST_BIT_CLKHI;
end else begin
timer <= timer + 26'd1;
end
end
// -- CLOCK high: rising edge shifts the bit in --
ST_BIT_CLKHI: begin
CLOCK <= 1'b1;
if (timer == SHIFT_HALF_PERIOD-1) begin
timer <= 26'd0;
row_shift <= {row_shift[6:0], 1'b0};
colg_shift <= {colg_shift[6:0], 1'b0};
colr_shift <= {colr_shift[6:0], 1'b0};
if (bit_idx == 3'd7) begin
bit_idx <= 3'd0;
state <= ST_LATCH_SETUP;
end else begin
bit_idx <= bit_idx + 3'd1;
state <= ST_BIT_SETUP;
end
end else begin
timer <= timer + 26'd1;
end
end
// ---- RCLOCK low, settle ----
ST_LATCH_SETUP: begin
CLOCK <= 1'b0;
RCLOCK <= 1'b0;
if (timer == SHIFT_HALF_PERIOD-1) begin
timer <= 26'd0;
state <= ST_LATCH_HI;
end else begin
timer <= timer + 26'd1;
end
end
// ---- RCLOCK high: rising edge latches storage regs ----
ST_LATCH_HI: begin
RCLOCK <= 1'b1;
if (timer == SHIFT_HALF_PERIOD-1) begin
timer <= 26'd0;
state <= ST_LATCH_LO;
end else begin
timer <= timer + 26'd1;
end
end
// ---- RCLOCK back low ----
ST_LATCH_LO: begin
RCLOCK <= 1'b0;
if (timer == SHIFT_HALF_PERIOD-1) begin
timer <= 26'd0;
state <= ST_HOLD;
end else begin
timer <= timer + 26'd1;
end
end
// -- hold this row visible, then advance to next row --
ST_HOLD: begin
if (timer == ROW_HOLD_TICKS-1) begin
timer <= 26'd0;
row_idx <= row_idx + 3'd1;
state <= ST_LOAD;
end else begin
timer <= timer + 26'd1;
end
end
default: state <= ST_CLR;
endcase
end
end
endmodule
`default_nettype wire広げる
●フォントROM
`default_nettype none
// コメント省略
module font_rom (
input logic [3:0] digit, // 0-9 (values 10-15 are unused -> blank)
input logic [2:0] row, // 0-7, top to bottom
output logic [7:0] pattern
);
always_comb begin
unique case (digit)
4'd0: begin
unique case (row)
3'd0: pattern = 8'h3C;//00111100
3'd1: pattern = 8'h66;//01100110
3'd2: pattern = 8'h66;//01100110
3'd3: pattern = 8'h66;//01100110
3'd4: pattern = 8'h66;//01100110
3'd5: pattern = 8'h66;//01100110
3'd6: pattern = 8'h66;//01100110
3'd7: pattern = 8'h3C;//00111100
default: pattern = 8'h00;
endcase
end
4'd1: begin
unique case (row)
3'd0: pattern = 8'h18;//00011000
3'd1: pattern = 8'h38;//00111000
3'd2: pattern = 8'h18;//00011000
3'd3: pattern = 8'h18;//00011000
3'd4: pattern = 8'h18;//00011000
3'd5: pattern = 8'h18;//00011000
3'd6: pattern = 8'h18;//00011000
3'd7: pattern = 8'h3C;//00110100
default: pattern = 8'h00;
endcase
end
4'd2: begin
unique case (row)
3'd0: pattern = 8'h3C;
3'd1: pattern = 8'h66;
3'd2: pattern = 8'h06;
3'd3: pattern = 8'h0C;
3'd4: pattern = 8'h18;
3'd5: pattern = 8'h30;
3'd6: pattern = 8'h60;
3'd7: pattern = 8'h7E;
default: pattern = 8'h00;
endcase
end
4'd3: begin
unique case (row)
3'd0: pattern = 8'h3C;
3'd1: pattern = 8'h66;
3'd2: pattern = 8'h06;
3'd3: pattern = 8'h1C;
3'd4: pattern = 8'h06;
3'd5: pattern = 8'h06;
3'd6: pattern = 8'h66;
3'd7: pattern = 8'h3C;
default: pattern = 8'h00;
endcase
end
4'd4: begin
unique case (row)
3'd0: pattern = 8'h0C;
3'd1: pattern = 8'h1C;
3'd2: pattern = 8'h3C;
3'd3: pattern = 8'h6C;
3'd4: pattern = 8'h7E;
3'd5: pattern = 8'h0C;
3'd6: pattern = 8'h0C;
3'd7: pattern = 8'h0C;
default: pattern = 8'h00;
endcase
end
4'd5: begin
unique case (row)
3'd0: pattern = 8'h7E;
3'd1: pattern = 8'h60;
3'd2: pattern = 8'h60;
3'd3: pattern = 8'h7C;
3'd4: pattern = 8'h06;
3'd5: pattern = 8'h06;
3'd6: pattern = 8'h66;
3'd7: pattern = 8'h3C;
default: pattern = 8'h00;
endcase
end
4'd6: begin
unique case (row)
3'd0: pattern = 8'h1C;
3'd1: pattern = 8'h30;
3'd2: pattern = 8'h60;
3'd3: pattern = 8'h7C;
3'd4: pattern = 8'h66;
3'd5: pattern = 8'h66;
3'd6: pattern = 8'h66;
3'd7: pattern = 8'h3C;
default: pattern = 8'h00;
endcase
end
4'd7: begin
unique case (row)
3'd0: pattern = 8'h7E;
3'd1: pattern = 8'h06;
3'd2: pattern = 8'h0C;
3'd3: pattern = 8'h18;
3'd4: pattern = 8'h30;
3'd5: pattern = 8'h30;
3'd6: pattern = 8'h30;
3'd7: pattern = 8'h30;
default: pattern = 8'h00;
endcase
end
4'd8: begin
unique case (row)
3'd0: pattern = 8'h3C;
3'd1: pattern = 8'h66;
3'd2: pattern = 8'h66;
3'd3: pattern = 8'h3C;
3'd4: pattern = 8'h66;
3'd5: pattern = 8'h66;
3'd6: pattern = 8'h66;
3'd7: pattern = 8'h3C;
default: pattern = 8'h00;
endcase
end
4'd9: begin
unique case (row)
3'd0: pattern = 8'h3C;
3'd1: pattern = 8'h66;
3'd2: pattern = 8'h66;
3'd3: pattern = 8'h66;
3'd4: pattern = 8'h3E;
3'd5: pattern = 8'h06;
3'd6: pattern = 8'h0C;
3'd7: pattern = 8'h38;
default: pattern = 8'h00;
endcase
end
default: pattern = 8'h00; // digits 10-15: blank (unused)
endcase
end
endmodule
`default_nettype wire
広げる