xref: /XiangShan/src/main/scala/device/AXI4VGA.scala (revision 8891a219bbc84f568e1d134854d8d5ed86d6d560)
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 org.chipsalliance.cde.config.Parameters
22import chisel3.experimental.ExtModule
23import freechips.rocketchip.amba.axi4.{AXI4AdapterNode, AXI4IdentityNode, AXI4Parameters, AXI4SlaveNode, AXI4SlaveParameters, AXI4SlavePortParameters, AXI4Xbar}
24import freechips.rocketchip.diplomacy.{AddressSet, LazyModule, LazyModuleImp, RegionType}
25import utils._
26import utility._
27
28trait HasVGAConst {
29  val ScreenW = 800
30  val ScreenH = 600
31
32  val HFrontPorch = 56
33  val HActive = HFrontPorch + 120
34  val HBackPorch = HActive + ScreenW
35  val HTotal = HBackPorch + 64
36  val VFrontPorch = 37
37  val VActive = VFrontPorch + 6
38  val VBackPorch = VActive + ScreenH
39  val VTotal = VBackPorch + 23
40}
41
42trait HasHDMIConst {
43  val ScreenW = 800
44  val ScreenH = 600
45
46  val HFrontPorch = 40
47  val HActive = HFrontPorch + 128
48  val HBackPorch = HActive + ScreenW
49  val HTotal = HBackPorch + 88
50  val VFrontPorch = 1
51  val VActive = VFrontPorch + 4
52  val VBackPorch = VActive + ScreenH
53  val VTotal = VBackPorch + 23
54}
55
56trait HasVGAParameter extends HasHDMIConst {
57  val FBWidth = ScreenW / 2
58  val FBHeight = ScreenH / 2
59  val FBPixels = FBWidth * FBHeight
60}
61
62class VGABundle extends Bundle {
63  val rgb = Output(UInt(24.W))
64  val hsync = Output(Bool())
65  val vsync = Output(Bool())
66  val valid = Output(Bool())
67}
68
69class VGACtrlBundle extends Bundle {
70  val sync = Output(Bool())
71}
72
73class VGACtrl
74(
75  address: Seq[AddressSet]
76)(implicit p: Parameters)
77  extends AXI4SlaveModule(address, _extra = new VGACtrlBundle, executable = false) with HasVGAParameter {
78  override lazy val module = new AXI4SlaveModuleImp[VGACtrlBundle](this) {
79
80    val fbSizeReg = Cat(FBWidth.U(16.W), FBHeight.U(16.W))
81    val sync = in.aw.fire
82
83    val mapping = Map(
84      RegMap(0x0, fbSizeReg, RegMap.Unwritable),
85      RegMap(0x4, sync, RegMap.Unwritable)
86    )
87
88    RegMap.generate(mapping, raddr(3, 0), in.r.bits.data,
89      waddr(3, 0), in.w.fire, in.w.bits.data, MaskExpand(in.w.bits.strb))
90
91    io.extra.get.sync := sync
92  }
93}
94
95class FBHelper extends ExtModule with HasExtModuleInline {
96  val clk = IO(Input(Clock()))
97  val valid = IO(Input(Bool()))
98  val pixel = IO(Input(UInt(32.W)))
99  val sync = IO(Input(Bool()))
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.clk := clock
193      fbHelper.valid := io.vga.valid
194      fbHelper.pixel := color
195      fbHelper.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