xref: /XiangShan/src/main/scala/device/AXI4VGA.scala (revision c6d439803a044ea209139672b25e35fe8d7f4aa0)
1/***************************************************************************************
2* Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences
3*
4* XiangShan is licensed under Mulan PSL v2.
5* You can use this software according to the terms and conditions of the Mulan PSL v2.
6* You may obtain a copy of Mulan PSL v2 at:
7*          http://license.coscl.org.cn/MulanPSL2
8*
9* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
10* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
11* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
12*
13* See the Mulan PSL v2 for more details.
14***************************************************************************************/
15
16package device
17
18import chisel3._
19import chisel3.util._
20import chipsalliance.rocketchip.config.Parameters
21import freechips.rocketchip.amba.axi4.{AXI4AdapterNode, AXI4IdentityNode, AXI4Parameters, AXI4SlaveNode, AXI4SlaveParameters, AXI4SlavePortParameters, AXI4Xbar}
22import freechips.rocketchip.diplomacy.{AddressSet, LazyModule, LazyModuleImp, RegionType}
23import utils._
24
25trait HasVGAConst {
26  val ScreenW = 800
27  val ScreenH = 600
28
29  val HFrontPorch = 56
30  val HActive = HFrontPorch + 120
31  val HBackPorch = HActive + ScreenW
32  val HTotal = HBackPorch + 64
33  val VFrontPorch = 37
34  val VActive = VFrontPorch + 6
35  val VBackPorch = VActive + ScreenH
36  val VTotal = VBackPorch + 23
37}
38
39trait HasHDMIConst {
40  val ScreenW = 800
41  val ScreenH = 600
42
43  val HFrontPorch = 40
44  val HActive = HFrontPorch + 128
45  val HBackPorch = HActive + ScreenW
46  val HTotal = HBackPorch + 88
47  val VFrontPorch = 1
48  val VActive = VFrontPorch + 4
49  val VBackPorch = VActive + ScreenH
50  val VTotal = VBackPorch + 23
51}
52
53trait HasVGAParameter extends HasHDMIConst {
54  val FBWidth = ScreenW / 2
55  val FBHeight = ScreenH / 2
56  val FBPixels = FBWidth * FBHeight
57}
58
59class VGABundle extends Bundle {
60  val rgb = Output(UInt(24.W))
61  val hsync = Output(Bool())
62  val vsync = Output(Bool())
63  val valid = Output(Bool())
64}
65
66class VGACtrlBundle extends Bundle {
67  val sync = Output(Bool())
68}
69
70class VGACtrl
71(
72  address: Seq[AddressSet]
73)(implicit p: Parameters)
74  extends AXI4SlaveModule(address, _extra = new VGACtrlBundle, executable = false) with HasVGAParameter {
75  override lazy val module = new AXI4SlaveModuleImp[VGACtrlBundle](this) {
76
77    val fbSizeReg = Cat(FBWidth.U(16.W), FBHeight.U(16.W))
78    val sync = in.aw.fire()
79
80    val mapping = Map(
81      RegMap(0x0, fbSizeReg, RegMap.Unwritable),
82      RegMap(0x4, sync, RegMap.Unwritable)
83    )
84
85    RegMap.generate(mapping, raddr(3, 0), in.r.bits.data,
86      waddr(3, 0), in.w.fire(), in.w.bits.data, MaskExpand(in.w.bits.strb))
87
88    io.extra.get.sync := sync
89  }
90}
91
92class FBHelper extends BlackBox with HasBlackBoxInline {
93  val io = IO(new Bundle {
94    val clk = Input(Clock())
95    val valid = Input(Bool())
96    val pixel = Input(UInt(32.W))
97    val sync = Input(Bool())
98  })
99
100  setInline("FBHelper.v",
101    s"""
102       |import "DPI-C" function void put_pixel(input int pixel);
103       |import "DPI-C" function void vmem_sync();
104       |
105       |module FBHelper (
106       |  input clk,
107       |  input valid,
108       |  input [31:0] pixel,
109       |  input sync
110       |);
111       |
112       |  always@(posedge clk) begin
113       |    if (valid) put_pixel(pixel);
114       |    if (sync) vmem_sync();
115       |  end
116       |
117       |endmodule
118     """.stripMargin)
119}
120
121class AXI4VGA
122(
123  sim: Boolean = false,
124  fbAddress: Seq[AddressSet],
125  ctrlAddress: Seq[AddressSet]
126)(implicit p: Parameters)
127  extends LazyModule with HasVGAParameter {
128
129
130  private val fb = LazyModule(new AXI4RAM(
131    fbAddress,
132    memByte= FBPixels * 4,
133    sim,
134    executable = false
135  ))
136  private val ctrl = LazyModule(new VGACtrl(ctrlAddress))
137
138  val node = AXI4IdentityNode()
139
140  fb.node := node
141  ctrl.node := node
142
143  lazy val module = new LazyModuleImp(this) {
144
145    val io = IO(new Bundle() {
146      val vga = new VGABundle
147    })
148
149    val out_fb = node.out.head._1
150    val out_ctrl = node.out.last._1
151    val in_fb = node.in.head._1
152    val in_ctrl = node.in.last._1
153
154    in_fb.ar.ready := true.B
155    in_fb.r.bits.data := 0.U
156    in_fb.r.bits.resp := AXI4Parameters.RESP_OKAY
157    in_fb.r.valid := BoolStopWatch(in_fb.ar.fire(), in_fb.r.fire(), startHighPriority = true)
158
159    def inRange(x: UInt, start: Int, end: Int) = (x >= start.U) && (x < end.U)
160
161    val (hCounter, hFinish) = Counter(true.B, HTotal)
162    val (vCounter, vFinish) = Counter(hFinish, VTotal)
163    io.vga.hsync := hCounter >= HFrontPorch.U
164    io.vga.vsync := vCounter >= VFrontPorch.U
165
166    val hInRange = inRange(hCounter, HActive, HBackPorch)
167    val vInRange = inRange(vCounter, VActive, VBackPorch)
168    io.vga.valid := hInRange && vInRange
169
170    val hCounterIsOdd = hCounter(0)
171    val hCounterIs2 = hCounter(1, 0) === 2.U
172    val vCounterIsOdd = vCounter(0)
173    // there is 2 cycle latency to read block memory,
174    // so we should issue the read request 2 cycle eariler
175    val nextPixel = inRange(hCounter, HActive - 1, HBackPorch - 1) && vInRange && hCounterIsOdd
176    val fbPixelAddrV0 = Counter(nextPixel && !vCounterIsOdd, FBPixels)._1
177    val fbPixelAddrV1 = Counter(nextPixel && vCounterIsOdd, FBPixels)._1
178
179    //   each pixel is 4 bytes
180    out_fb.ar.bits.prot := 0.U
181    out_fb.ar.bits.addr := Cat(Mux(vCounterIsOdd, fbPixelAddrV1, fbPixelAddrV0), 0.U(2.W))
182    out_fb.ar.valid := RegNext(nextPixel) && hCounterIs2
183
184    out_fb.r.ready := true.B
185    val data = HoldUnless(out_fb.r.bits.data, out_fb.r.fire())
186    val color = Mux(hCounter(1), data(63, 32), data(31, 0))
187    io.vga.rgb := Mux(io.vga.valid, color(23, 0), 0.U)
188
189    if (sim) {
190      val fbHelper = Module(new FBHelper)
191      fbHelper.io.clk := clock
192      fbHelper.io.valid := io.vga.valid
193      fbHelper.io.pixel := color
194      fbHelper.io.sync := ctrl.module.io.extra.get.sync
195    }
196
197  }
198
199  //  val AXIidBits = 2
200  //  val io = IO(new Bundle {
201  //    val in = new Bundle {
202  //      val fb = Flipped(new AXI4Lite)
203  //      val ctrl = Flipped(new AXI4Lite)
204  //    }
205  //    val vga = new VGABundle
206  //  })
207  //
208  //  val ctrl = Module(new VGACtrl)
209  //  io.in.ctrl <> ctrl.io.in
210  //  val fb = Module(new AXI4RAM(new AXI4Lite, memByte = FBPixels * 4))
211  //  // writable by axi4lite
212  //  // but it only readable by the internel controller
213  //  fb.io.in.aw <> io.in.fb.aw
214  //  fb.io.in.w <> io.in.fb.w
215  //  io.in.fb.b <> fb.io.in.b
216  //  io.in.fb.ar.ready := true.B
217  //  io.in.fb.r.bits.data := 0.U
218  //  io.in.fb.r.bits.resp := AXI4Parameters.RESP_OKAY
219  //  io.in.fb.r.valid := BoolStopWatch(io.in.fb.ar.fire(), io.in.fb.r.fire(), startHighPriority = true)
220  //
221  //  def inRange(x: UInt, start: Int, end: Int) = (x >= start.U) && (x < end.U)
222  //
223  //  val (hCounter, hFinish) = Counter(true.B, HTotal)
224  //  val (vCounter, vFinish) = Counter(hFinish, VTotal)
225  //
226  //  io.vga.hsync := hCounter >= HFrontPorch.U
227  //  io.vga.vsync := vCounter >= VFrontPorch.U
228  //
229  //  val hInRange = inRange(hCounter, HActive, HBackPorch)
230  //  val vInRange = inRange(vCounter, VActive, VBackPorch)
231  //  io.vga.valid := hInRange && vInRange
232  //
233  //  val hCounterIsOdd = hCounter(0)
234  //  val hCounterIs2 = hCounter(1,0) === 2.U
235  //  val vCounterIsOdd = vCounter(0)
236  //  // there is 2 cycle latency to read block memory,
237  //  // so we should issue the read request 2 cycle eariler
238  //  val nextPixel = inRange(hCounter, HActive - 1, HBackPorch - 1) && vInRange && hCounterIsOdd
239  //  val fbPixelAddrV0 = Counter(nextPixel && !vCounterIsOdd, FBPixels)._1
240  //  val fbPixelAddrV1 = Counter(nextPixel &&  vCounterIsOdd, FBPixels)._1
241  //
242  //  // each pixel is 4 bytes
243  //  fb.io.in.ar.bits.prot := 0.U
244  //  fb.io.in.ar.bits.addr := Cat(Mux(vCounterIsOdd, fbPixelAddrV1, fbPixelAddrV0), 0.U(2.W))
245  //  fb.io.in.ar.valid := RegNext(nextPixel) && hCounterIs2
246  //
247  //  fb.io.in.r.ready := true.B
248  //  val data = HoldUnless(fb.io.in.r.bits.data, fb.io.in.r.fire())
249  //  val color = Mux(hCounter(1), data(63, 32), data(31, 0))
250  //  io.vga.rgb := Mux(io.vga.valid, color(23, 0), 0.U)
251  //
252  //  if (sim) {
253  //    val fbHelper = Module(new FBHelper)
254  //    fbHelper.io.clk := clock
255  //    fbHelper.io.valid := io.vga.valid
256  //    fbHelper.io.pixel := color
257  //    fbHelper.io.sync := ctrl.io.extra.get.sync
258  //  }
259}
260