xref: /XiangShan/src/main/scala/device/AXI4VGA.scala (revision 66314a3840e5ebe6cc81ca4725bde95942f67489)
1package device
2
3import chisel3._
4import chisel3.util._
5
6import bus.axi4._
7import utils._
8
9trait HasVGAConst {
10  val ScreenW = 800
11  val ScreenH = 600
12
13  val HFrontPorch = 56
14  val HActive = HFrontPorch + 120
15  val HBackPorch = HActive + ScreenW
16  val HTotal = HBackPorch + 64
17  val VFrontPorch = 37
18  val VActive = VFrontPorch + 6
19  val VBackPorch = VActive + ScreenH
20  val VTotal = VBackPorch + 23
21}
22
23trait HasHDMIConst {
24  val ScreenW = 800
25  val ScreenH = 600
26
27  val HFrontPorch = 40
28  val HActive = HFrontPorch + 128
29  val HBackPorch = HActive + ScreenW
30  val HTotal = HBackPorch + 88
31  val VFrontPorch = 1
32  val VActive = VFrontPorch + 4
33  val VBackPorch = VActive + ScreenH
34  val VTotal = VBackPorch + 23
35}
36
37trait HasVGAParameter extends HasHDMIConst {
38  val FBWidth = ScreenW / 2
39  val FBHeight = ScreenH / 2
40  val FBPixels = FBWidth * FBHeight
41}
42
43class VGABundle extends Bundle {
44  val rgb = Output(UInt(24.W))
45  val hsync = Output(Bool())
46  val vsync = Output(Bool())
47  val valid = Output(Bool())
48}
49
50class VGACtrlBundle extends Bundle {
51  val sync = Output(Bool())
52}
53
54class VGACtrl extends AXI4SlaveModule(new AXI4Lite, new VGACtrlBundle) with HasVGAParameter {
55  val fbSizeReg = Cat(FBWidth.U(16.W), FBHeight.U(16.W))
56  val sync = in.aw.fire()
57
58  val mapping = Map(
59    RegMap(0x0, fbSizeReg, RegMap.Unwritable),
60    RegMap(0x4, sync, RegMap.Unwritable)
61  )
62
63  RegMap.generate(mapping, raddr(3,0), in.r.bits.data,
64    waddr(3,0), in.w.fire(), in.w.bits.data, MaskExpand(in.w.bits.strb))
65
66  io.extra.get.sync := sync
67}
68
69class FBHelper extends BlackBox with HasBlackBoxInline {
70  val io = IO(new Bundle {
71    val clk = Input(Clock())
72    val valid = Input(Bool())
73    val pixel = Input(UInt(32.W))
74    val sync = Input(Bool())
75  })
76
77  setInline("FBHelper.v",
78    s"""
79      |import "DPI-C" function void put_pixel(input int pixel);
80      |import "DPI-C" function void vmem_sync();
81      |
82      |module FBHelper (
83      |  input clk,
84      |  input valid,
85      |  input [31:0] pixel,
86      |  input sync
87      |);
88      |
89      |  always@(posedge clk) begin
90      |    if (valid) put_pixel(pixel);
91      |    if (sync) vmem_sync();
92      |  end
93      |
94      |endmodule
95     """.stripMargin)
96}
97
98class AXI4VGA(sim: Boolean = false) extends Module with HasVGAParameter {
99  val AXIidBits = 2
100  val io = IO(new Bundle {
101    val in = new Bundle {
102      val fb = Flipped(new AXI4Lite)
103      val ctrl = Flipped(new AXI4Lite)
104    }
105    val vga = new VGABundle
106  })
107
108  val ctrl = Module(new VGACtrl)
109  io.in.ctrl <> ctrl.io.in
110  val fb = Module(new AXI4RAM(new AXI4Lite, memByte = FBPixels * 4))
111  // writable by axi4lite
112  // but it only readable by the internel controller
113  fb.io.in.aw <> io.in.fb.aw
114  fb.io.in.w <> io.in.fb.w
115  io.in.fb.b <> fb.io.in.b
116  io.in.fb.ar.ready := true.B
117  io.in.fb.r.bits.data := 0.U
118  io.in.fb.r.bits.resp := AXI4Parameters.RESP_OKAY
119  io.in.fb.r.valid := BoolStopWatch(io.in.fb.ar.fire(), io.in.fb.r.fire(), startHighPriority = true)
120
121  def inRange(x: UInt, start: Int, end: Int) = (x >= start.U) && (x < end.U)
122
123  val (hCounter, hFinish) = Counter(true.B, HTotal)
124  val (vCounter, vFinish) = Counter(hFinish, VTotal)
125
126  io.vga.hsync := hCounter >= HFrontPorch.U
127  io.vga.vsync := vCounter >= VFrontPorch.U
128
129  val hInRange = inRange(hCounter, HActive, HBackPorch)
130  val vInRange = inRange(vCounter, VActive, VBackPorch)
131  io.vga.valid := hInRange && vInRange
132
133  val hCounterIsOdd = hCounter(0)
134  val hCounterIs2 = hCounter(1,0) === 2.U
135  val vCounterIsOdd = vCounter(0)
136  // there is 2 cycle latency to read block memory,
137  // so we should issue the read request 2 cycle eariler
138  val nextPixel = inRange(hCounter, HActive - 1, HBackPorch - 1) && vInRange && hCounterIsOdd
139  val fbPixelAddrV0 = Counter(nextPixel && !vCounterIsOdd, FBPixels)._1
140  val fbPixelAddrV1 = Counter(nextPixel &&  vCounterIsOdd, FBPixels)._1
141
142  // each pixel is 4 bytes
143  fb.io.in.ar.bits.prot := 0.U
144  fb.io.in.ar.bits.addr := Cat(Mux(vCounterIsOdd, fbPixelAddrV1, fbPixelAddrV0), 0.U(2.W))
145  fb.io.in.ar.valid := RegNext(nextPixel) && hCounterIs2
146
147  fb.io.in.r.ready := true.B
148  val data = HoldUnless(fb.io.in.r.bits.data, fb.io.in.r.fire())
149  val color = Mux(hCounter(1), data(63, 32), data(31, 0))
150  io.vga.rgb := Mux(io.vga.valid, color(23, 0), 0.U)
151
152  if (sim) {
153    val fbHelper = Module(new FBHelper)
154    fbHelper.io.clk := clock
155    fbHelper.io.valid := io.vga.valid
156    fbHelper.io.pixel := color
157    fbHelper.io.sync := ctrl.io.extra.get.sync
158  }
159}
160