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