xref: /XiangShan/src/main/scala/device/AXI4VGA.scala (revision 5a7b942b030b799f8086bf9a6096981200fb8b46)
1package device
2
3import chisel3._
4import chisel3.util._
5
6import bus.axi4._
7import utils._
8
9trait HasVGAConst {
10  // these are only fit for 800x600
11  val ScreenW = 800
12  val ScreenH = 600
13
14  val HFrontPorch = 56
15  val HActive = HFrontPorch + 120
16  val HBackPorch = HActive + ScreenW
17  val HTotal = HBackPorch + 64
18  val VFrontPorch = 37
19  val VActive = VFrontPorch + 6
20  val VBackPorch = VActive + ScreenH
21  val VTotal = VBackPorch + 23
22
23  val FBWidth = ScreenW / 2
24  val FBHeight = ScreenH / 2
25  val FBPixels = FBWidth * FBHeight
26}
27
28class VGABundle extends Bundle {
29  val r = Output(UInt(4.W))
30  val g = Output(UInt(4.W))
31  val b = Output(UInt(4.W))
32  val hsync = Output(Bool())
33  val vsync = Output(Bool())
34}
35
36class VGACtrl extends AXI4SlaveModule(new AXI4Lite) with HasVGAConst {
37  // actually this is a constant
38  val fbSizeReg = Cat(FBWidth.U(16.W), FBHeight.U(16.W))
39  // we always return fbSizeReg to axi4lite
40  in.r.bits.data := fbSizeReg
41  val sync = in.aw.fire()
42}
43
44class AXI4VGA extends Module with HasVGAConst {
45  // need a 50MHz clock
46  val io = IO(new Bundle {
47    val in = new Bundle {
48      val fb = Flipped(new AXI4Lite)
49      val ctrl = Flipped(new AXI4Lite)
50    }
51    val vga = new VGABundle
52  })
53
54  val ctrl = Module(new VGACtrl)
55  io.in.ctrl <> ctrl.io.in
56  val fb = Module(new AXI4RAM(_type = new AXI4Lite, FBPixels * 4))
57  // writable by axi4lite
58  // but it only readable by the internel controller
59  fb.io.in.aw <> io.in.fb.aw
60  fb.io.in.w <> io.in.fb.w
61  io.in.fb.b <> fb.io.in.b
62  io.in.fb.ar.ready := true.B
63  io.in.fb.r.bits.data := 0.U
64  io.in.fb.r.bits.resp := AXI4Parameters.RESP_OKAY
65  io.in.fb.r.valid := BoolStopWatch(io.in.fb.ar.fire(), io.in.fb.r.fire(), startHighPriority = true)
66
67  def inRange(x: UInt, start: Int, end: Int) = (x >= start.U) && (x < end.U)
68
69  val (hCounter, hFinish) = Counter(true.B, HTotal)
70  val (vCounter, vFinish) = Counter(hFinish, VTotal)
71
72  io.vga.hsync := hCounter >= HFrontPorch.U
73  io.vga.vsync := vCounter >= VFrontPorch.U
74
75  val hInRange = inRange(hCounter, HActive, HBackPorch)
76  val vInRange = inRange(vCounter, VActive, VBackPorch)
77  val videoValid = hInRange && vInRange
78
79  val hCounterIsOdd = hCounter(0)
80  val vCounterIsOdd = vCounter(0)
81  // there is 1 cycle latency to read block memory,
82  // so we should issue the read request 1 cycle eariler
83  val nextPixel = inRange(hCounter, HActive - 1, HBackPorch - 1) && vInRange && hCounterIsOdd
84  val fbPixelAddrV0 = Counter(nextPixel && !vCounterIsOdd, FBPixels)._1
85  val fbPixelAddrV1 = Counter(nextPixel &&  vCounterIsOdd, FBPixels)._1
86
87  // each pixel is 4 bytes
88  fb.io.in.ar.bits.addr := Cat(Mux(vCounterIsOdd, fbPixelAddrV1, fbPixelAddrV0), 0.U(2.W))
89  fb.io.in.ar.bits.prot := DontCare
90  fb.io.in.ar.valid := nextPixel
91
92  fb.io.in.r.ready := true.B
93  val color = fb.io.in.r.bits.data
94  io.vga.r := Mux(videoValid, color(23, 20), 0.U)
95  io.vga.g := Mux(videoValid, color(15, 12), 0.U)
96  io.vga.b := Mux(videoValid, color(7, 4), 0.U)
97}
98