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