Files
2025-yatcpu/lab2/朱梓涵24325356/scala/peripheral/HDMIDisplay.scala
2025-11-18 20:25:21 +08:00

395 lines
11 KiB
Scala

// Copyright 2022 hrpccs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package peripheral
import chisel3._
import chisel3.util._
class HDMISync extends Module {
val io = IO(new Bundle {
val hsync = Output(Bool())
val vsync = Output(Bool())
val video_on = Output(Bool())
val p_tick = Output(Bool())
val f_tick = Output(Bool())
val x = Output(UInt(10.W))
val y = Output(UInt(10.W))
val x_next = Output(UInt(10.W))
val y_next = Output(UInt(10.W))
})
val DisplayHorizontal = ScreenInfo.DisplayHorizontal
val DisplayVertical = ScreenInfo.DisplayVertical
val BorderLeft = 48
val BorderRight = 16
val BorderTop = 10
val BorderBottom = 33
val RetraceHorizontal = 96
val RetraceVertical = 2
val MaxHorizontal = DisplayHorizontal + BorderLeft + BorderRight + RetraceHorizontal - 1
val MaxVertical = DisplayVertical + BorderTop + BorderBottom + RetraceVertical - 1
val RetraceHorizontalStart = DisplayHorizontal + BorderRight
val RetraceHorizontalEnd = RetraceHorizontalStart + RetraceHorizontal - 1
val RetraceVerticalStart = DisplayVertical + BorderBottom
val RetraceVerticalEnd = RetraceVerticalStart + RetraceVertical - 1
val pixel = RegInit(UInt(3.W), 0.U)
val pixel_next = Wire(UInt(3.W))
val pixel_tick = Wire(Bool())
val v_count_reg = RegInit(UInt(10.W), 0.U)
val h_count_reg = RegInit(UInt(10.W), 0.U)
val v_count_next = Wire(UInt(10.W))
val h_count_next = Wire(UInt(10.W))
val vsync_reg = RegInit(Bool(), false.B)
val hsync_reg = RegInit(Bool(), false.B)
val vsync_next = Wire(Bool())
val hsync_next = Wire(Bool())
pixel_next := Mux(pixel === 4.U, 0.U, pixel + 1.U)
pixel_tick := pixel === 0.U
h_count_next := Mux(
pixel_tick,
Mux(h_count_reg === MaxHorizontal.U, 0.U, h_count_reg + 1.U),
h_count_reg
)
v_count_next := Mux(
pixel_tick && h_count_reg === MaxHorizontal.U,
Mux(v_count_reg === MaxVertical.U, 0.U, v_count_reg + 1.U),
v_count_reg
)
hsync_next := h_count_reg >= RetraceHorizontalStart.U && h_count_reg <= RetraceHorizontalEnd.U
vsync_next := v_count_reg >= RetraceVerticalStart.U && v_count_reg <= RetraceVerticalEnd.U
pixel := pixel_next
hsync_reg := hsync_next
vsync_reg := vsync_next
v_count_reg := v_count_next
h_count_reg := h_count_next
io.video_on := h_count_reg < DisplayHorizontal.U && v_count_reg < DisplayVertical.U
io.hsync := hsync_reg
io.vsync := vsync_reg
io.x := h_count_reg
io.y := v_count_reg
io.x_next := h_count_next
io.y_next := v_count_next
io.p_tick := pixel_tick
io.f_tick := io.x === 0.U && io.y === 0.U
}
class TMDS_encoder extends Module {
val io = IO(new Bundle() {
val video_data = Input(UInt(8.W)) //r,g,b,8bit
val control_data = Input(UInt(2.W))
val video_on = Input(Bool())
val TMDS = Output(UInt(10.W))
})
val Nb1s = PopCount(io.video_data)
val xored = xorfct(io.video_data)
val xnored = xnorfct(io.video_data)
val XNOR = (Nb1s > 4.U(4.W)) || (Nb1s === 4.U(4.W) && io.video_data(0) === 0.U)
val q_m = RegInit(0.U(9.W))
val diffSize = 4
val diff = RegInit(0.S(diffSize.W))
q_m := Mux(
XNOR,
xnored,
xored
)
val disparitySize = 4
val disparityReg = RegInit(0.S(disparitySize.W))
diff := PopCount(q_m).asSInt - 4.S
val doutReg = RegInit("b1010101011".U(10.W))
def xorfct(value: UInt): UInt = {
val vin = VecInit(value.asBools)
val res = VecInit(511.U.asBools)
res(0) := vin(0)
for (i <- 1 to 7) {
res(i) := res(i - 1) ^ vin(i)
}
res(8) := 1.U
res.asUInt
}
def xnorfct(value: UInt): UInt = {
val vin = VecInit(value.asBools)
val res = VecInit(511.U.asBools)
res(0) := vin(0)
for (i <- 1 to 7) {
res(i) := !(res(i - 1) ^ vin(i))
}
res(8) := 0.U
res.asUInt
}
when(io.video_on === false.B) {
disparityReg := 0.S
doutReg := "b1010101011".U(10.W)
switch(io.control_data) {
is("b00".U(2.W)) {
doutReg := "b1101010100".U(10.W)
}
is("b01".U(2.W)) {
doutReg := "b0010101011".U(10.W)
}
is("b10".U(2.W)) {
doutReg := "b0101010100".U(10.W)
}
}
}.otherwise {
when(disparityReg === 0.S || diff === 0.S) {
when(q_m(8) === false.B) {
doutReg := "b10".U(2.W) ## ~q_m(7, 0)
disparityReg := disparityReg - diff
}.otherwise {
doutReg := "b01".U(2.W) ## q_m(7, 0)
disparityReg := disparityReg + diff
}
}.elsewhen((!diff(diffSize - 1) && !disparityReg(disparitySize - 1))
|| (diff(diffSize - 1) && disparityReg(disparitySize - 1))) {
doutReg := 1.U(1.W) ## q_m(8) ## ~q_m(7, 0)
when(q_m(8)) {
disparityReg := disparityReg + 1.S - diff
}.otherwise {
disparityReg := disparityReg - diff
}
}.otherwise {
doutReg := 0.U(1.W) ## q_m
when(q_m(8)) {
disparityReg := disparityReg + diff
}.otherwise {
disparityReg := disparityReg - 1.S + diff
}
}
}
io.TMDS := doutReg
}
class HDMIDisplay extends Module {
val io = IO(new Bundle() {
val rgb = Input(UInt(24.W))
val x = Output(UInt(16.W))
val y = Output(UInt(16.W))
val x_next = Output(UInt(16.W))
val y_next = Output(UInt(16.W))
val video_on = Output(Bool())
val TMDSclk_p = Output(Bool())
val TMDSdata_p = Output(UInt(3.W))
val TMDSclk_n = Output(Bool())
val TMDSdata_n = Output(UInt(3.W))
})
val rgb = io.rgb
val pixel_clk = Wire(Bool())
val hsync = Wire(Bool())
val vsync = Wire(Bool())
val sync = Module(new HDMISync)
io.x := sync.io.x
io.y := sync.io.y
io.x_next := sync.io.x_next
io.y_next := sync.io.y_next
io.video_on := sync.io.video_on
hsync := sync.io.hsync
vsync := sync.io.vsync
pixel_clk := sync.io.p_tick
// TMDS_PLLVR is a vivado IP core, check it in /verilog/pynq/TMDS_PLLVR.v
val serial_clk = Wire(Clock())
val pll_lock = Wire(Bool())
val tmdspll = Module(new TMDS_PLLVR)
val rst = Wire(Reset())
tmdspll.io.clkin := pixel_clk.asClock
serial_clk := tmdspll.io.clkout
pll_lock := tmdspll.io.lock
tmdspll.io.reset := reset
rst := ~pll_lock
val tmds = Wire(UInt(3.W))
val tmds_clk = Wire(Bool())
withClockAndReset(pixel_clk.asClock, rst) {
val tmds_channel1 = Wire(UInt(10.W))
val tmds_channel2 = Wire(UInt(10.W))
val tmds_channel0 = Wire(UInt(10.W))
val tmds_green = Module(new TMDS_encoder)
val tmds_red = Module(new TMDS_encoder)
val tmds_blue = Module(new TMDS_encoder)
tmds_red.io.video_on := sync.io.video_on
tmds_blue.io.video_on := sync.io.video_on
tmds_green.io.video_on := sync.io.video_on
tmds_blue.io.control_data := sync.io.vsync ## sync.io.hsync
tmds_green.io.control_data := 0.U
tmds_red.io.control_data := 0.U
tmds_red.io.video_data := rgb(23, 16)
tmds_blue.io.video_data := rgb(7, 0)
tmds_green.io.video_data := rgb(15, 8)
tmds_channel0 := tmds_blue.io.TMDS
tmds_channel1 := tmds_green.io.TMDS
tmds_channel2 := tmds_red.io.TMDS
val serdesBlue = Module(new Oser10Module())
serdesBlue.io.data := tmds_channel0
serdesBlue.io.fclk := serial_clk
val serdesGreen = Module(new Oser10Module())
serdesGreen.io.data := tmds_channel1
serdesGreen.io.fclk := serial_clk
val serdesRed = Module(new Oser10Module())
serdesRed.io.data := tmds_channel2
serdesRed.io.fclk := serial_clk
tmds := serdesRed.io.q ## serdesGreen.io.q ## serdesBlue.io.q
//serdesCLk : 25Mhz ,Why not directly use p_tick?
//cause Duty Ratio of p_tick is 10% , while which of serdesCLk is 50%
val serdesClk = Module(new Oser10Module())
serdesClk.io.data := "b1111100000".U(10.W)
serdesClk.io.fclk := serial_clk
tmds_clk := serdesClk.io.q
val buffDiffBlue = Module(new OBUFDS)
buffDiffBlue.io.I := tmds(0)
val buffDiffGreen = Module(new OBUFDS)
buffDiffGreen.io.I := tmds(1)
val buffDiffRed = Module(new OBUFDS)
buffDiffRed.io.I := tmds(2)
val buffDiffClk = Module(new OBUFDS)
buffDiffClk.io.I := tmds_clk
io.TMDSclk_p := buffDiffClk.io.O
io.TMDSclk_n := buffDiffClk.io.OB
io.TMDSdata_p := buffDiffRed.io.O ## buffDiffGreen.io.O ## buffDiffBlue.io.O
io.TMDSdata_n := buffDiffRed.io.OB ## buffDiffGreen.io.OB ## buffDiffBlue.io.OB
}
}
//----------------------------------------
//PLL frequency multiplier using BlackBox
class TMDS_PLLVR extends BlackBox {
val io = IO(new Bundle {
val clkin = Input(Clock())
val reset = Input(Reset())
val clkout = Output(Clock())
val clkoutd = Output(Clock())
val lock = Output(Bool())
})
}
/* OSER10 : serializer 10:1*/
class OSER10 extends Module {
val io = IO(new Bundle {
val Q = Output(Bool()) // OSER10 data output signal
val D0 = Input(Bool())
val D1 = Input(Bool())
val D2 = Input(Bool())
val D3 = Input(Bool())
val D4 = Input(Bool())
val D5 = Input(Bool())
val D6 = Input(Bool())
val D7 = Input(Bool())
val D8 = Input(Bool())
val D9 = Input(Bool()) // OSER10 data input signal
val PCLK = Input(Clock()) // Primary clock input signal
val FCLK = Input(Clock()) // High speed clock input signal
val RESET = Input(Reset()) // Asynchronous reset input signal,
//active-high.
})
withClockAndReset(io.FCLK, io.RESET) {
val count = RegInit(0.U(4.W))
val countnext = Wire(UInt(4.W))
io.Q := MuxLookup(
count,
0.U,
IndexedSeq(
0.U -> io.D0.asBool,
1.U -> io.D1.asBool,
2.U -> io.D2.asBool,
3.U -> io.D3.asBool,
4.U -> io.D4.asBool,
5.U -> io.D5.asBool,
6.U -> io.D6.asBool,
7.U -> io.D7.asBool,
8.U -> io.D8.asBool,
9.U -> io.D9.asBool
)
)
countnext := Mux(
count === 9.U, 0.U, count + 1.U
)
count := countnext
}
}
class Oser10Module extends Module {
val io = IO(new Bundle {
val q = Output(Bool())
val data = Input(UInt(10.W))
val fclk = Input(Clock()) // Fast clock
})
val osr10 = Module(new OSER10())
io.q := osr10.io.Q
osr10.io.D0 := io.data(0)
osr10.io.D1 := io.data(1)
osr10.io.D2 := io.data(2)
osr10.io.D3 := io.data(3)
osr10.io.D4 := io.data(4)
osr10.io.D5 := io.data(5)
osr10.io.D6 := io.data(6)
osr10.io.D7 := io.data(7)
osr10.io.D8 := io.data(8)
osr10.io.D9 := io.data(9)
osr10.io.PCLK := clock
osr10.io.FCLK := io.fclk
osr10.io.RESET := reset
}
/* lvds output */
class OBUFDS extends BlackBox {
val io = IO(new Bundle {
val O = Output(Bool())
val OB = Output(Bool())
val I = Input(Bool())
})
}
//-----------------------------------------
//-----------------------------------------