バージョン:2024/10/07
目次:
ドットクロック周波数 2,200 × 1,125 × 60 [Hz] = 148.5 [MHz]
| 信号 | 本数 | 内容 |
|---|---|---|
| hSync | 1 | 水平同期信号 |
| vSync | 1 | 垂直同期信号 |
| colorR | 4 | 赤色 |
| colorG | 4 | 緑色 |
| colorB | 4 | 青色 |
class PmodVga extends Module {
val io = IO(new Bundle {
val hSync = Output(UInt(1.W))
val vSync = Output(UInt(1.W))
val colorR = Output(UInt(4.W))
val colorG = Output(UInt(4.W))
val colorB = Output(UInt(4.W))
})
/* Horizontal */
val hDispWidth = 10.U(12.W) /* テスト用に小さい数値で設定 */
val hFrontPorch = 3.U(12.W)
val hSyncWidth = 3.U(12.W)
val hTotalWidth = 20.U(12.W)
val hCountReg = RegInit(0.U(12.W))
hCountReg := Mux(hCountReg < (hTotalWidth - 1.U), hCountReg + 1.U, 0.U)
val hSyncReg = RegInit(1.U(1.W))
hSyncReg := Mux(hCountReg >= (hDispWidth + hFrontPorch - 1.U) &&
hCountReg < (hDispWidth + hFrontPorch + hSyncWidth - 1.U),
0.U, 1.U)
io.hSync := hSyncReg
/* Vertical parameters */
val vDispHeight = 6.U(12.W) /* テスト用に小さい数値で設定 */
val vFrontPorch = 1.U(12.W)
val vSyncWidth = 2.U(12.W)
val vTotalHeight = 10.U(12.W)
val vCountReg = RegInit(0.U(12.W))
val vSyncReg = RegInit(1.U(1.W))
when (hCountReg === (hDispWidth + hFrontPorch - 1.U)) { /* カウンタの更新タイミングはHSYNC出力開始時と同じ */
vCountReg := Mux(vCountReg < (vTotalHeight - 1.U), vCountReg + 1.U, 0.U)
vSyncReg := Mux(vCountReg >= (vDispHeight + vFrontPorch - 1.U) &&
vCountReg < (vDispHeight + vFrontPorch + vSyncWidth - 1.U),
0.U, 1.U)
}
io.vSync := vSyncReg
/* Other signals */
io.colorR := 0.U
io.colorG := 0.U
io.colorB := 0.U
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
class PmodVgaTest extends AnyFlatSpec with ChiselScalatestTester {
behavior of "PmodVga"
it should "pass" in {
test(new PmodVga) { c =>
c.clock.setTimeout(0)
val hTotal = 20
val vTotal = 10
val frameH = Array.ofDim[BigInt](vTotal, hTotal)
val frameV = Array.ofDim[BigInt](vTotal, hTotal)
val lineV = Array.ofDim[BigInt](vTotal)
var hSyncPrev = BigInt(-1)
/* Test */
for (y <- 0 until vTotal) {
for (x <- 0 until hTotal) {
c.clock.step(1)
val hSyncNow = c.io.hSync.peek().litValue
val vSyncNow = c.io.vSync.peek().litValue
frameH(y)(x) = hSyncNow
frameV(y)(x) = vSyncNow
if ((hSyncPrev === 1) && (hSyncNow === 0)) {
lineV(y) = vSyncNow
}
hSyncPrev = hSyncNow
}
}
/* Print */
println("HSYNC frame")
for (y <- 0 until vTotal) {
print(lineV(y))
print(": ")
for (x <- 0 until hTotal) {
print(frameH(y)(x))
}
print("¥n")
}
/* Print vSync frame */
println("VSYNC frame")
for (y <- 0 until vTotal) {
for (x <- 0 until hTotal) {
print(frameV(y)(x))
}
print("¥n")
}
println("¥nEnd of test")
}
}
}
$ sbt test
[info] welcome to sbt 1.10.2 (Ubuntu Java 17.0.12)
[info] loading project definition from /home/masatakak/work/chisel/pmod-vga/project
[info] loading settings for project pmod-vga from build.sbt ...
[info] set current project to pmod-vga (in build file:/home/masatakak/work/chisel/pmod-vga/)
[info] compiling 1 Scala source to /home/masatakak/work/chisel/pmod-vga/target/scala-2.12/classes ...
[info] compiling 1 Scala source to /home/masatakak/work/chisel/pmod-vga/target/scala-2.12/test-classes ...
start the Pmod VGA
HSYNC frame
1: 11111111111100011111
1: 11111111111100011111
1: 11111111111100011111
1: 11111111111100011111
1: 11111111111100011111
1: 11111111111100011111
0: 11111111111100011111
0: 11111111111100011111
1: 11111111111100011111
1: 11111111111100011111
VSYNC frame
11111111111111111111
11111111111111111111
11111111111111111111
11111111111111111111
11111111111111111111
11111111111111111111
11111111111100000000
00000000000000000000
00000000000011111111
11111111111111111111
End of test
/*
* vga_top.v
*/
module vga_top(input CLK_I,
output [3:0] VGA_R, output [3:0] VGA_G, output [3:0] VGA_B,
output VGA_HS_O, output VGA_VS_O);
wire res;
wire clk_vga; /* VGA clock */
wire locked; /* PLL lock status (not used) */
assign res = 1'h0;
clk_wiz_0 pll(.clk_out1(clk_vga), .reset(res),
.clk_in1(CLK_I), .locked(locked));
PmodVga vgactl(.clock(clk_vga), .reset(res),
.io_hSync(VGA_HS_O), .io_vSync(VGA_VS_O),
.io_colorR(VGA_R), .io_colorG(VGA_G), .io_colorB(VGA_B));
endmodule
# Add Sources
read_verilog {../../PmodVga.v}
read_verilog {../../verilog/vga_top.v}
# 1. Verilogファイル名の変更
# Add IPs
create_ip -name clk_wiz -vendor xilinx.com -library ip -module_name clk_wiz_0
set_property -dict [list CONFIG.PRIM_IN_FREQ {100.00} ¥
CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {148.500}] [get_ips clk_wiz_0]
generate_target {all} [get_ips clk_wiz_0]+
# 2. クロック周波数変更
read_verilog {./arty_a7.gen/sources_1/ip/clk_wiz_0/clk_wiz_0.v}
read_verilog {./arty_a7.gen/sources_1/ip/clk_wiz_0/clk_wiz_0_clk_wiz.v}
# Add constraints
read_xdc Arty-A7-100-Master.xdc
set_property PROCESSING_ORDER EARLY [get_files Arty-A7-100-Master.xdc]
# Add pre-synthesis commands
# Synthesis
synth_design -directive default -top vga_top -part xc7a100tcsg324-1
# 3. トップレベルのモジュール名変更
/* RGB signals */
val active = Mux((hCountReg < hDispWidth) && (vCountReg < vDispHeight), 1.U, 0.U)
val colSelect = hCountReg(5, 4)
val colValue = hCountReg(3, 0)
when (active === 1.U) {
io.colorR := Mux(colSelect === 0.U || colSelect === 3.U, colValue, 0.U)
io.colorG := Mux(colSelect === 1.U || colSelect === 3.U, colValue, 0.U)
io.colorB := Mux(colSelect === 2.U || colSelect === 3.U, colValue, 0.U)
} .otherwise {
io.colorR := 0.U
io.colorG := 0.U
io.colorB := 0.U
}
/* RGB signals */
val active = Mux((hCountReg < hDispWidth) && (vCountReg < vDispHeight), 1.U, 0.U)
when (active === 1.U) {
io.colorR := Mux(hCountReg < 512.U && vCountReg < 256.U && hCountReg(8) === 1.U, hCountReg(5, 2),
Mux(hCountReg < 512.U && vCountReg < 256.U && pixelInBox === 0.U, 15.U,
Mux((hCountReg >= 512.U && vCountReg(8) === 1.U && hCountReg(3) === 1.U) ||
(hCountReg >= 512.U && vCountReg(8) === 0.U && vCountReg(3) === 1.U), 15.U, 0.U)))
io.colorB := Mux(hCountReg < 512.U && vCountReg < 256.U && hCountReg(6) === 1.U, hCountReg(5, 2),
Mux(hCountReg < 512.U && vCountReg < 256.U && pixelInBox === 0.U, 15.U,
Mux((hCountReg >= 512.U && vCountReg(8) === 1.U && hCountReg(3) === 1.U) ||
(hCountReg >= 512.U && vCountReg(8) === 0.U && vCountReg(3) === 1.U), 15.U, 0.U)))
io.colorG := Mux(hCountReg < 512.U && vCountReg < 256.U && hCountReg(7) === 1.U, hCountReg(5, 2),
Mux(hCountReg < 512.U && vCountReg < 256.U && pixelInBox === 0.U, 15.U,
Mux((hCountReg >= 512.U && vCountReg(8) === 1.U && hCountReg(3) === 1.U) ||
(hCountReg >= 512.U && vCountReg(8) === 0.U && vCountReg(3) === 1.U), 15.U, 0.U)))
} .otherwise {
io.colorR := 0.U
io.colorG := 0.U
io.colorB := 0.U
}
/* Box */
val boxWidth = 8.U
val boxXMax = 512.U - boxWidth
val boxYMax = vDispHeight - boxWidth
val boxXMin = 0.U
val boxYMin = 256.U
val boxXInit = 0.U(12.W)
val boxYInit = 400.U(12.W)
val boxClockDiv = 1000000.U
val boxCountReg = RegInit(0.U(25.W))
boxCountReg := Mux(boxCountReg === boxClockDiv - 1.U, 0.U, boxCountReg + 1.U)
val updateBox = Mux(boxCountReg === boxClockDiv - 1.U, 1.U, 0.U)
val boxXReg = RegInit(0.U(12.W))
val boxYReg = RegInit(0.U(12.W))
val boxXDirReg = RegInit(1.U(1.W))
val boxYDirReg = RegInit(1.U(1.W))
when (updateBox === 1.U) {
boxXReg := Mux(boxXDirReg === 1.U, boxXReg + 1.U, boxXReg - 1.U)
boxYReg := Mux(boxYDirReg === 1.U, boxYReg + 1.U, boxYReg - 1.U)
boxXDirReg := Mux(boxXDirReg === 1.U && boxXReg === (boxXMax - 1.U), 0.U,
Mux(boxXDirReg === 0.U && boxXReg === (boxXMin + 1.U), 1.U, boxXDirReg))
boxYDirReg := Mux(boxYDirReg === 1.U && boxYReg === (boxYMax - 1.U), 0.U,
Mux(boxYDirReg === 0.U && boxYReg === (boxYMin + 1.U), 1.U, boxYDirReg))
}
val pixelInBox = Mux(hCountReg >= boxXReg && hCountReg < (boxXReg + boxWidth) &&
vCountReg >= boxYReg && vCountReg < (boxYReg + boxWidth), 1.U, 0.U)
when (active === 1.U) {
io.colorR := Mux(pixelInBox === 1.U, 15.U,
Mux(hCountReg < 512.U && vCountReg < 256.U && hCountReg(8) === 1.U, hCountReg(5, 2),
Mux(hCountReg < 512.U && vCountReg < 256.U && pixelInBox === 0.U, 15.U,
Mux((hCountReg >= 512.U && vCountReg(8) === 1.U && hCountReg(3) === 1.U) ||
(hCountReg >= 512.U && vCountReg(8) === 0.U && vCountReg(3) === 1.U), 15.U, 0.U))))
Copyright © Japan Embedded Systems Technology Association All Rights Reserved.