blob: c6c3907a5564b21f920ae0500be81e473f9f6224 [file] [log] [blame]
// SPDX-FileCopyrightText: 2020 Muhammad Hadir Khan
//
// 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.
// SPDX-License-Identifier: Apache-2.0
package ibtida_soc
import Chisel.Fill
import chisel3._
import chisel3.util.{Cat, Enum, log2Ceil}
import _root_.core.Core
import gpio.Gpio
import merl.uit.tilelink.{TLConfiguration, TLSocket1_N, TL_H2D, TL_HostAdapter, TL_RegAdapter, TL_SramAdapter}
import primitives.{DataMem, InstMem}
import uart0.UartController
class Ibtida_top(implicit val conf: TLConfiguration) extends Module {
val io = IO(new Bundle {
val rx_i = Input(UInt(1.W))
val CLK_PER_BIT = Input(UInt(16.W))
val gpio_i = Input(UInt(32.W))
val gpio_o = Output(UInt(32.W))
val gpio_en_o = Output(UInt(32.W))
// instruction memory interface
val iccm_we_o = Output(Vec(4, Bool()))
val iccm_wdata_o = Output(UInt(32.W))
val iccm_addr_o = Output(UInt(8.W))
val iccm_rdata_i = Input(UInt(32.W))
// data memory interface
val dccm_we_o = Output(Vec(4, Bool()))
val dccm_wdata_o = Output(UInt(32.W))
val dccm_addr_o = Output(UInt(8.W))
val dccm_rdata_i = Input(UInt(32.W))
})
val uart_ctrl = Module(new UartController)
uart_ctrl.io.CLK_PER_BIT := io.CLK_PER_BIT
//val uart_ctrl = Module(new UartController(10000, 3000))
val core = Module(new Core())
//val iccm = Module(new InstMem())
//val dccm = Module(new DataMem())
val gpio = Module(new Gpio())
val core_iccm_tl_host = Module(new TL_HostAdapter())
val core_loadStore_tl_host = Module(new TL_HostAdapter())
val iccm_tl_device = Module(new TL_SramAdapter(sramAw = 6, sramDw = 32, forFetch = true.B)) // for 64 words memory
val dccm_tl_device = Module(new TL_SramAdapter(sramAw = 6, sramDw = 32)) // for 64 words memory
val tl_switch_1to2 = Module(new TLSocket1_N(2))
/** ||||||||||||||||||||||||||||||| INITIAL BOOT UP AFTER RESET ||||||||||||||||||||||||||||||| */
val instr_we = Wire(Vec(4,Bool()))
val instr_wdata = Wire(UInt(32.W))
val instr_addr = Wire(UInt(32.W))
val rx_data_reg = RegInit(0.U(32.W))
val rx_addr_reg = RegInit(0.U(32.W))
val reset_reg = RegInit(true.B)
uart_ctrl.io.rxd := io.rx_i
val idle :: read_uart :: write_iccm :: prog_finish :: Nil = Enum(4)
val state_reg = RegInit(idle)
reset_reg := reset.asBool()
when(state_reg === idle) {
// checking to see if the reset button was pressed previously and now it falls back to 0 for starting the read uart condition
when(reset_reg === true.B && reset.asBool() === false.B) {
state_reg := read_uart
} .otherwise {
state_reg := idle
}
// setting all we_i to be false, since nothing to be written
instr_we.foreach(w => w := false.B)
//instr_we := false.B // active high
instr_addr := iccm_tl_device.io.addr_o
instr_wdata := DontCare
core.io.stall_core_i := false.B
uart_ctrl.io.isStalled := false.B
} .elsewhen(state_reg === read_uart) {
// when valid 32 bits available the next state would be writing into the ICCM.
when(uart_ctrl.io.valid) {
state_reg := write_iccm
} .elsewhen(uart_ctrl.io.done) {
// if getting done signal it means the read_uart state got a special ending instruction which means the
// program is finish and no need to write to the iccm so the next state would be prog_finish
state_reg := prog_finish
} .otherwise {
// if not getting valid or done it means the 32 bits have not yet been read by the UART.
// so the next state would still be read_uart
state_reg := read_uart
}
// setting all we_i to be false, since nothing to be written
instr_we.foreach(w => w := false.B)
//instr_we := true.B // active low
instr_addr := DontCare
instr_wdata := DontCare
core.io.stall_core_i := true.B
uart_ctrl.io.isStalled := true.B
// store data and addr in registers if uart_ctrl.valid is high to save it since going to next state i.e write_iccm
// will take one more cycle which may make the received data and addr invalid since by then another data and addr
// could be written inside it.
rx_data_reg := Mux(uart_ctrl.io.valid, uart_ctrl.io.rx_data_o, 0.U)
// rx_addr_reg := Mux(uart_ctrl.io.valid, uart_ctrl.io.addr_o << 2, 0.U) // left shifting address by 2 since uart ctrl sends address in 0,1,2... format but we need it in word aligned so 1 translated to 4, 2 translates to 8 (dffram requirement)
rx_addr_reg := Mux(uart_ctrl.io.valid, uart_ctrl.io.addr_o, 0.U)
} .elsewhen(state_reg === write_iccm) {
// when writing to the iccm state checking if the uart received the ending instruction. If it does then
// the next state would be prog_finish and if it doesn't then we move to the read_uart state again to
// read the next instruction
when(uart_ctrl.io.done) {
state_reg := prog_finish
} .otherwise {
state_reg := read_uart
}
// setting all we_i to be true, since instruction (32 bit) needs to be written
instr_we.foreach(w => w := true.B)
//instr_we := false.B // active low
instr_wdata := rx_data_reg
instr_addr := rx_addr_reg
// keep stalling the core
core.io.stall_core_i := true.B
uart_ctrl.io.isStalled := true.B
} .elsewhen(state_reg === prog_finish) {
// setting all we_i to be false, since nothing to be written
instr_we.foreach(w => w := false.B)
//instr_we := true.B // active low
instr_wdata := DontCare
instr_addr := iccm_tl_device.io.addr_o
core.io.stall_core_i := false.B
uart_ctrl.io.isStalled := false.B
state_reg := idle
} .otherwise {
// setting all we_i to be false, since nothing to be written
instr_we.foreach(w => w := false.B)
//instr_we := true.B // active low
instr_wdata := DontCare
instr_addr := DontCare
core.io.stall_core_i := DontCare
uart_ctrl.io.isStalled := DontCare
}
/** |||||||||||| ADDRESS DECODING FOR DEVICE SELECTION IN THE 1:2 TL SWITCH |||||||||||||| */
// setting up the dev_sel wire width according to the number of devices (N) in the DeviceMap + 1 for the error responder device
val N = TL_Peripherals.deviceMap.size
val dev_sel = Wire(UInt(log2Ceil(N+1).W))
// getting the address from the host adapter connected with the load/store unit of the core
val addr = core_loadStore_tl_host.io.tl_o.a_address
when((addr & ~AddressMap.ADDR_MASK_GPIO) === AddressMap.ADDR_SPACE_GPIO) {
dev_sel := TL_Peripherals.deviceMap("gpio")
}.elsewhen((addr & ~AddressMap.ADDR_MASK_DCCM) === AddressMap.ADDR_SPACE_DCCM) {
dev_sel := TL_Peripherals.deviceMap("dccm")
} .otherwise {
// if no address maps to the connected device's base address then route the host request to the error responder
dev_sel := N.asUInt()
}
/** ||||||||||||| CONNECTING THE TL-SWITCH TO THE HOST AND PERIPHERALS ||||||||||||||| */
// providing device select for request routing
tl_switch_1to2.io.dev_sel := dev_sel
// providing the host request from core's load/store unit to the TL switch
tl_switch_1to2.io.tl_h_i <> core_loadStore_tl_host.io.tl_o
// connecting the response of devices GPIO/DCCM from the switch to the core's load/store unit.
core_loadStore_tl_host.io.tl_i <> tl_switch_1to2.io.tl_h_o
// CONNECTING DCCM
// connecting the DCCM TL device adapter to the port 0 of the TL switch
dccm_tl_device.io.tl_i <> tl_switch_1to2.io.tl_d_o(0)
// taking the response from the DCCM TL device and connecting it with port 0 of TL switch
tl_switch_1to2.io.tl_d_i(0) <> dccm_tl_device.io.tl_o
// CONNECTING GPIO
// connecting the GPIO TL device port to the port 1 of the TL switch
gpio.io.tl_i <> tl_switch_1to2.io.tl_d_o(1)
// taking the response from the GPIO TL device port and connecting it with port 1 of TL switch
tl_switch_1to2.io.tl_d_i(1) <> gpio.io.tl_o
/** |||||||||||| CORE -> TL_HOST ADAPTER -> TL_DEVICE ADAPTER -> ICCM |||||||||||| */
/** |||||||||||| ICCM -> TL_DEVICE ADAPTER -> TL_HOST ADAPTER -> CORE |||||||||||| */
core_iccm_tl_host.io.req_i := core.io.instr_req_o
core_iccm_tl_host.io.addr_i := core.io.instr_addr_o
core_iccm_tl_host.io.we_i := false.B
core_iccm_tl_host.io.wdata_i := 0.U
core_iccm_tl_host.io.be_i := "b1111".U
iccm_tl_device.io.tl_i <> core_iccm_tl_host.io.tl_o
core_iccm_tl_host.io.tl_i <> iccm_tl_device.io.tl_o
// changing here right now
io.iccm_we_o := instr_we
io.iccm_addr_o := instr_addr
io.iccm_wdata_o := instr_wdata
// change ended here
//iccm.io.en_i := true.B // active high
//iccm.io.we_i := instr_we
//iccm.io.addr_i := instr_addr
//iccm.io.wdata_i := instr_wdata
// iccm_tl_device.io.rdata_i := iccm.io.rdata_o
iccm_tl_device.io.rdata_i := io.iccm_rdata_i
core.io.instr_rdata_i := core_iccm_tl_host.io.rdata_o
core.io.instr_gnt_i := core_iccm_tl_host.io.gnt_o
core.io.instr_rvalid_i := core_iccm_tl_host.io.valid_o
/** |||||||||||| CORE -> TL_HOST ADAPTER -> TL_DEVICE ADAPTER -> DCCM |||||||||||| */
/** |||||||||||| DCCM -> TL_DEVICE ADAPTER -> TL_HOST ADAPTER -> CORE |||||||||||| */
core_loadStore_tl_host.io.req_i := core.io.data_req_o
core_loadStore_tl_host.io.addr_i := core.io.data_addr_o.asUInt()
core_loadStore_tl_host.io.we_i := core.io.data_we_o
core_loadStore_tl_host.io.wdata_i:= core.io.data_wdata_o.asUInt()
core_loadStore_tl_host.io.be_i := Cat(core.io.data_be_o(3),core.io.data_be_o(2),core.io.data_be_o(1),core.io.data_be_o(0))
// changing here
// dccm_tl_device.io.rdata_i := Cat(dccm.io.rdata_o(3),dccm.io.rdata_o(2),dccm.io.rdata_o(1),dccm.io.rdata_o(0))
dccm_tl_device.io.rdata_i := Cat(io.dccm_rdata_i(3),io.dccm_rdata_i(2),io.dccm_rdata_i(1),io.dccm_rdata_i(0))
io.dccm_addr_o := dccm_tl_device.io.addr_o
io.dccm_wdata_o := dccm_tl_device.io.wdata_o
io.dccm_we_o := dccm_tl_device.io.we_o
//dccm.io.en_i := true.B // always enabling the memory (active high)
//dccm.io.addr_i := dccm_tl_device.io.addr_o
//dccm.io.wdata_i(0) := dccm_tl_device.io.wdata_o(7,0)
//dccm.io.wdata_i(1) := dccm_tl_device.io.wdata_o(15,8)
//dccm.io.wdata_i(2) := dccm_tl_device.io.wdata_o(23,16)
//dccm.io.wdata_i(3) := dccm_tl_device.io.wdata_o(31,24)
//dccm.io.we_i := dccm_tl_device.io.we_o
/** ||||||||||||||||| INITIALIZING THE GPIO ||||||||||||||||| */
//gpio.io.cio_gpio_i := Cat(Fill(28, 0.U),io.gpio_i)
gpio.io.cio_gpio_i := io.gpio_i
// val gpio_val = gpio.io.cio_gpio_o & gpio.io.cio_gpio_en_o
io.gpio_o := gpio.io.cio_gpio_o
io.gpio_en_o := ~gpio.io.cio_gpio_en_o // inverting since caravel takes en as active low
core.io.irq_external_i := gpio.io.intr_gpio_o.orR()
core.io.data_gnt_i := core_loadStore_tl_host.io.gnt_o
core.io.data_rvalid_i := core_loadStore_tl_host.io.valid_o
core.io.data_rdata_i := core_loadStore_tl_host.io.rdata_o.asSInt()
// dummy interface
//io.result := core.io.reg_7(3,0).asSInt()
}