diff --git a/lab1/src/main/scala/board/z710/z710/Top.scala b/lab1/src/main/scala/board/z710/z710/Top.scala index fc7a85d..497168e 100644 --- a/lab1/src/main/scala/board/z710/z710/Top.scala +++ b/lab1/src/main/scala/board/z710/z710/Top.scala @@ -1,119 +1,103 @@ +// Copyright 2022 Canbin Huang +// +// 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 board.z710 - import chisel3._ -import chisel3.stage.ChiselStage -import chisel3.util._ -import chisel3.{ChiselEnum, _} - -// import circt.stage.ChiselStage -import chisel3.stage.ChiselGeneratorAnnotation - -import bus._ +import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} +import chisel3.util.Cat import peripheral._ -import riscv._ import riscv.Parameters -import riscv.core.CPU -import javax.print.SimpleDoc +import riscv.core.{CPU, ProgramCounter} -object BootStates extends ChiselEnum { - val Init, Loading, BusWait, Finished = Value -} - - -class Top(binaryFilename: String ="say_goodbye.asmbin") extends Module { - // val binaryFilename = "say_goodbye.asmbin" - val io = IO(new Bundle { - // val switch = Input(UInt(16.W)) - - // val rgb = Output(UInt(12.W)) - - val led = Output(Bool()) +class Top(binaryFilename: String = "say_goodbye.asmbin") extends Module { + val io = IO(new Bundle() { val tx = Output(Bool()) val rx = Input(Bool()) - + val led = Output(Bool()) // z710 has few LEDs, use one for running indicator }) - val boot_state = RegInit(BootStates.Init) + val mem = Module(new Memory(Parameters.MemorySizeInWords)) + // val hdmi_display = Module(new HDMIDisplay) + // val display = Module(new CharacterDisplay) + // val timer = Module(new Timer) + val uart = Module(new Uart(frequency = 32_000000, baudRate = 115200)) // 31M or 32M is good, 33M more error + val dummy = Module(new Dummy) - val uart = Module(new Uart(125_000_000, 115200)) // this freq is consistent with Zynq 7 PS UART module + // display.io.bundle <> dummy.io.bundle + mem.io.bundle <> dummy.io.bundle + mem.io.debug_read_address := 0.U + // timer.io.bundle <> dummy.io.bundle + uart.io.bundle <> dummy.io.bundle io.tx := uart.io.txd uart.io.rxd := io.rx - val cpu = Module(new CPU) - val mem = Module(new Memory(Parameters.MemorySizeInWords)) - val timer = Module(new Timer) - val dummy = Module(new DummySlave) - val bus_arbiter = Module(new BusArbiter) - val bus_switch = Module(new BusSwitch) - val instruction_rom = Module(new InstructionROM(binaryFilename)) val rom_loader = Module(new ROMLoader(instruction_rom.capacity)) - bus_arbiter.io.bus_request(0) := true.B - - bus_switch.io.master <> cpu.io.axi4_channels - bus_switch.io.address := cpu.io.bus_address - for (i <- 0 until Parameters.SlaveDeviceCount) { - bus_switch.io.slaves(i) <> dummy.io.channels - } - rom_loader.io.load_address := Parameters.EntryAddress - rom_loader.io.load_start := false.B rom_loader.io.rom_data := instruction_rom.io.data + rom_loader.io.load_address := Parameters.EntryAddress instruction_rom.io.address := rom_loader.io.rom_address - cpu.io.stall_flag_bus := true.B - cpu.io.instruction_valid := false.B - bus_switch.io.slaves(0) <> mem.io.channels - rom_loader.io.channels <> dummy.io.channels - switch(boot_state) { - is(BootStates.Init) { - rom_loader.io.load_start := true.B - boot_state := BootStates.Loading - rom_loader.io.channels <> mem.io.channels - } - is(BootStates.Loading) { - rom_loader.io.load_start := false.B - rom_loader.io.channels <> mem.io.channels - when(rom_loader.io.load_finished) { - boot_state := BootStates.Finished + + val CPU_clkdiv = RegInit(UInt(2.W),0.U) + val CPU_tick = Wire(Bool()) + val CPU_next = Wire(UInt(2.W)) + CPU_next := Mux(CPU_clkdiv === 3.U, 0.U, CPU_clkdiv + 1.U) + CPU_tick := CPU_clkdiv === 0.U + CPU_clkdiv := CPU_next + + withClock(CPU_tick.asClock) { + val cpu = Module(new CPU) + // cpu.io.interrupt_flag := Cat(uart.io.signal_interrupt, timer.io.signal_interrupt) + // cpu.io.csr_regs_debug_read_address := 0.U + // cpu.io.regs_debug_read_address := 0.U + cpu.io.debug_read_address := 0.U + // cpu.io.memory_bundle.read_data := 0.U + cpu.io.instruction_valid := rom_loader.io.load_finished + mem.io.instruction_address := cpu.io.instruction_address + cpu.io.instruction := mem.io.instruction + + when(!rom_loader.io.load_finished) { + rom_loader.io.bundle <> mem.io.bundle + cpu.io.memory_bundle.read_data := 0.U + }.otherwise { + rom_loader.io.bundle.read_data := 0.U + when(cpu.io.deviceSelect === 2.U) { // deviceSelect = highest 3 bits of address, thus 0x4000_0000 is mapped to UART + cpu.io.memory_bundle <> uart.io.bundle + }.otherwise { + cpu.io.memory_bundle <> mem.io.bundle } } - is(BootStates.Finished) { - cpu.io.stall_flag_bus := false.B - cpu.io.instruction_valid := true.B - } } - bus_switch.io.slaves(2) <> uart.io.channels - bus_switch.io.slaves(4) <> timer.io.channels - - cpu.io.interrupt_flag := Cat(uart.io.signal_interrupt, timer.io.signal_interrupt) - - cpu.io.debug_read_address := 0.U - mem.io.debug_read_address := 0.U - - - - val clock_freq = 100_000_000.U - + // LED, blinks every second + val clock_freq = 40_000_000.U val led_count = RegInit(0.U(32.W)) - when (led_count >= clock_freq) { // the led blinks every second, clock freq is 100M + when (led_count >= clock_freq) { led_count := 0.U }.otherwise { led_count := led_count + 1.U } - io.led := (led_count >= (clock_freq >> 1)) + } - - object VerilogGenerator extends App { - (new ChiselStage).execute( - Array("-X", "verilog", "--target-dir", "verilog/z710"), - Seq(ChiselGeneratorAnnotation(() => new Top())) // default bin file - ) - + (new ChiselStage).execute( + Array("-X", "verilog", "-td", "verilog/z710"), + Seq(ChiselGeneratorAnnotation(() => new Top("say_goodbye.asmbin"))) // program to run on CPU + ) } \ No newline at end of file diff --git a/lab1/src/main/scala/peripheral/UART.scala b/lab1/src/main/scala/peripheral/UART.scala new file mode 100644 index 0000000..2d47c57 --- /dev/null +++ b/lab1/src/main/scala/peripheral/UART.scala @@ -0,0 +1,204 @@ +// Copyright 2021 Howard Lau +// +// 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 UartIO extends DecoupledIO(UInt(8.W)) { +} + + +/** + * Transmit part of the UART. + * A minimal version without any additional buffering. + * Use a ready/valid handshaking. + */ +class Tx(frequency: Int, baudRate: Int) extends Module { + val io = IO(new Bundle { + val txd = Output(UInt(1.W)) + val channel = Flipped(new UartIO()) + + }) + + val BIT_CNT = ((frequency + baudRate / 2) / baudRate - 1).U + + val shiftReg = RegInit(0x7ff.U) + val cntReg = RegInit(0.U(20.W)) + val bitsReg = RegInit(0.U(4.W)) + + io.channel.ready := (cntReg === 0.U) && (bitsReg === 0.U) + io.txd := shiftReg(0) + + when(cntReg === 0.U) { + + cntReg := BIT_CNT + when(bitsReg =/= 0.U) { + val shift = shiftReg >> 1 + shiftReg := Cat(1.U, shift(9, 0)) + bitsReg := bitsReg - 1.U + }.otherwise { + when(io.channel.valid) { + shiftReg := Cat(Cat(3.U, io.channel.bits), 0.U) // two stop bits, data, one start bit + bitsReg := 11.U + }.otherwise { + shiftReg := 0x7ff.U + } + } + + }.otherwise { + cntReg := cntReg - 1.U + } +} + +/** + * Receive part of the UART. + * A minimal version without any additional buffering. + * Use a ready/valid handshaking. + * + * The following code is inspired by Tommy's receive code at: + * https://github.com/tommythorn/yarvi + */ +class Rx(frequency: Int, baudRate: Int) extends Module { + val io = IO(new Bundle { + val rxd = Input(UInt(1.W)) + val channel = new UartIO() + + }) + + val BIT_CNT = ((frequency + baudRate / 2) / baudRate - 1).U + val START_CNT = ((3 * frequency / 2 + baudRate / 2) / baudRate - 1).U + + // Sync in the asynchronous RX data, reset to 1 to not start reading after a reset + val rxReg = RegNext(RegNext(io.rxd, 1.U), 1.U) + + val shiftReg = RegInit(0.U(8.W)) + val cntReg = RegInit(0.U(20.W)) + val bitsReg = RegInit(0.U(4.W)) + val valReg = RegInit(false.B) + + when(cntReg =/= 0.U) { + cntReg := cntReg - 1.U + }.elsewhen(bitsReg =/= 0.U) { + cntReg := BIT_CNT + shiftReg := Cat(rxReg, shiftReg >> 1) + bitsReg := bitsReg - 1.U + // the last shifted in + when(bitsReg === 1.U) { + valReg := true.B + } + }.elsewhen(rxReg === 0.U) { // wait 1.5 bits after falling edge of start + cntReg := START_CNT + bitsReg := 8.U + } + + when(valReg && io.channel.ready) { + valReg := false.B + } + + io.channel.bits := shiftReg + io.channel.valid := valReg +} + +/** + * A single byte buffer with a ready/valid interface + */ +class Buffer extends Module { + val io = IO(new Bundle { + val in = Flipped(new UartIO()) + val out = new UartIO() + }) + + val empty :: full :: Nil = Enum(2) + val stateReg = RegInit(empty) + val dataReg = RegInit(0.U(8.W)) + + io.in.ready := stateReg === empty + io.out.valid := stateReg === full + + when(stateReg === empty) { + when(io.in.valid) { + dataReg := io.in.bits + stateReg := full + } + }.otherwise { // full + when(io.out.ready) { + stateReg := empty + } + } + io.out.bits := dataReg +} + +/** + * A transmitter with a single buffer. + */ +class BufferedTx(frequency: Int, baudRate: Int) extends Module { + val io = IO(new Bundle { + val txd = Output(UInt(1.W)) + val channel = Flipped(new UartIO()) + + }) + val tx = Module(new Tx(frequency, baudRate)) + val buf = Module(new Buffer) + + buf.io.in <> io.channel + tx.io.channel <> buf.io.out + io.txd <> tx.io.txd +} + +class Uart(frequency: Int, baudRate: Int) extends Module { + val io = IO(new Bundle { + val bundle = new RAMBundle + val rxd = Input(UInt(1.W)) + val txd = Output(UInt(1.W)) + + val signal_interrupt = Output(Bool()) + }) + val interrupt = RegInit(false.B) + val rxData = RegInit(0.U) + + val tx = Module(new BufferedTx(frequency, baudRate)) + val rx = Module(new Rx(frequency, baudRate)) + + io.bundle.read_data := 0.U + when(io.bundle.address === 0x4.U) { + io.bundle.read_data := baudRate.U + }.elsewhen(io.bundle.address === 0xC.U) { + io.bundle.read_data := rxData + interrupt := false.B + } + + tx.io.channel.valid := false.B + tx.io.channel.bits := 0.U + when(io.bundle.write_enable) { + when(io.bundle.address === 0x8.U) { + interrupt := io.bundle.write_data =/= 0.U + }.elsewhen(io.bundle.address === 0x10.U) { + tx.io.channel.valid := true.B + tx.io.channel.bits := io.bundle.write_data + } + } + + io.txd := tx.io.txd + rx.io.rxd := io.rxd + + io.signal_interrupt := interrupt + rx.io.channel.ready := false.B + when(rx.io.channel.valid) { + rx.io.channel.ready := true.B + rxData := rx.io.channel.bits + interrupt := true.B + } +}