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