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