Skip to content

Commit

Permalink
TL-UH implementation added via LFX Mentorship by RISC-V
Browse files Browse the repository at this point in the history
Add logics for Atomic Operation and Burst Messages. All tests passing.
  • Loading branch information
shahzaibk23 authored May 30, 2023
2 parents 67ce0e3 + 338e08a commit 6e1da05
Show file tree
Hide file tree
Showing 14 changed files with 1,304 additions and 173 deletions.
144 changes: 144 additions & 0 deletions src/main/resources/sram.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// SPDX-FileCopyrightText: 2020 fabless Corporation
//
// 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

//`default_nettype none
// OpenRAM SRAM model
// Words: 256
// Word size: 32
// Write size: 8

module sram #(
parameter NUM_WMASKS = 4,
parameter DATA_WIDTH = 32,
parameter ADDR_WIDTH = 13,
parameter RAM_DEPTH = 1 << ADDR_WIDTH,
// FIXME: This delay is arbitrary.
parameter DELAY = 3,
parameter IZERO = 0 , // binary / Initial RAM with zeros (has priority over INITFILE)
parameter IFILE = ""
)
(
/*`ifdef USE_POWER_PINS
vdd,
gnd,
`endif */
// Port 0: RW
clk0,csb0,web0,wmask0,addr0,din0,dout0,
// Port 1: R
clk1,csb1,addr1,dout1
);


/*`ifdef USE_POWER_PINS
inout vdd;
inout gnd;
`endif
*/
input clk0; // clock
input csb0; // active low chip select
input web0; // active low write control
input [NUM_WMASKS-1:0] wmask0; // write mask
input [ADDR_WIDTH-1:0] addr0;
input [DATA_WIDTH-1:0] din0;
output [DATA_WIDTH-1:0] dout0;
input clk1; // clock
input csb1; // active low chip select
input [ADDR_WIDTH-1:0] addr1;
output [DATA_WIDTH-1:0] dout1;

reg csb0_reg;
reg web0_reg;
reg [NUM_WMASKS-1:0] wmask0_reg;
reg [ADDR_WIDTH-1:0] addr0_reg;
reg [DATA_WIDTH-1:0] din0_reg;
reg [DATA_WIDTH-1:0] dout0;

// All inputs are registers
always @(posedge clk0)
begin
csb0_reg = csb0;
web0_reg = web0;
wmask0_reg = wmask0;
addr0_reg = addr0;
din0_reg = din0;
//dout0 = 32'bx0;
/*`ifdef DBG
if ( !csb0_reg && web0_reg )
$display($time," Reading %m addr0=%b dout0=%b",addr0_reg,mem[addr0_reg]);
if ( !csb0_reg && !web0_reg )
$display($time," Writing %m addr0=%b din0=%b wmask0=%b",addr0_reg,din0_reg,wmask0_reg);
`endif
*/ end

reg csb1_reg;
reg [ADDR_WIDTH-1:0] addr1_reg;
reg [DATA_WIDTH-1:0] dout1;

// All inputs are registers
always @(posedge clk1)
begin
csb1_reg = csb1;
addr1_reg = addr1;
//`ifdef DBG
// if (!csb0 && !web0 && !csb1 && (addr0 == addr1))
// $display($time," WARNING: Writing and reading addr0=%b and addr1=%b simultaneously!",addr0,addr1);
// dout1 = 32'bx;
// if ( !csb1_reg )
// $display($time," Reading %m addr1=%b dout1=%b",addr1_reg,mem[addr1_reg]);
//`endif
end
integer i;
reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];
initial
if (IZERO)
for (i=0; i<RAM_DEPTH; i=i+1) mem[i] = {DATA_WIDTH{1'b0}};
else
if (IFILE != "") $readmemh(IFILE, mem);

// Memory Write Block Port 0
// Write Operation : When web0 = 0, csb0 = 0
always @ (negedge clk0)
begin : MEM_WRITE0
if ( !csb0_reg && !web0_reg ) begin
if (wmask0_reg[0])
mem[addr0_reg][7:0] = din0_reg[7:0];
if (wmask0_reg[1])
mem[addr0_reg][15:8] = din0_reg[15:8];
if (wmask0_reg[2])
mem[addr0_reg][23:16] = din0_reg[23:16];
if (wmask0_reg[3])
mem[addr0_reg][31:24] = din0_reg[31:24];
end
end

// Memory Read Block Port 0
// Read Operation : When web0 = 1, csb0 = 0
always @ (negedge clk0)
begin : MEM_READ0
if (!csb0_reg && web0_reg)
dout0 <= #(DELAY) mem[addr0_reg];
end

// Memory Read Block Port 1
// Read Operation : When web1 = 1, csb1 = 0/
always @ (negedge clk1)
begin : MEM_READ1
if (!csb1_reg)
dout1 <= #(DELAY) mem[addr1_reg];
end

endmodule
//`default_nettype wire

80 changes: 80 additions & 0 deletions src/main/resources/sram_top.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
module sram_top #(
parameter IFILE_IN = ""
)
(
input logic clk_i,
input logic rst_i,

// sram interface in
input logic csb_i,
input logic [12:0] addr_i,
input logic [31:0] wdata_i,
input logic [3:0] wmask_i,
input logic we_i,
output logic [31:0] rdata_o

);

logic csb;
logic [12:0] addr_o;
logic [31:0] wdata_o;
logic [3:0] wmask_o;
logic we_o;
logic [31:0] rdata_i;

logic rvalid;

always_ff @(negedge clk_i) begin
if(rst_i) begin
csb <= '1;
addr_o <= '0;
wdata_o <= '0;
wmask_o <= '0;
we_o <= '1;
end else begin
csb <= csb_i;
addr_o <= addr_i;
wdata_o <= wdata_i;
wmask_o <= wmask_i;
we_o <= we_i;
end
end

sram #(
.NUM_WMASKS (4),
.DATA_WIDTH (32),
.ADDR_WIDTH (13),
.RAM_DEPTH (1 << 13),
// FIXME: This delay is arbitrary.
.DELAY (3),
.IZERO (0) , // binary / Initial RAM with zeros (has priority over INITFILE)
.IFILE (IFILE_IN)
) memory (
/*`ifdef USE_POWER_PINS
.vdd,
.gnd,
`endif */
.clk0(clk_i),
.csb0(csb_i),
.web0(we_i),
.wmask0(wmask_i),
.addr0(addr_i),
.din0(wdata_i),
.dout0(rdata_o),
.clk1('0),
.csb1('1),
.addr1('0),
.dout1()
);

always_ff @(posedge clk_i or posedge rst_i) begin
if (rst_i) begin
rvalid <= 1'b0;
end else if (!we_i) begin
rvalid <= 1'b0;
end else if(!csb_i && we_i) begin
rvalid <= 1'b1;
end
end

endmodule
66 changes: 65 additions & 1 deletion src/main/scala/caravan/bus/common/DummyMemory.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class DummyMemController/*(programFile: Option[String])*/(implicit val config: B
// holds the data in byte vectors read from memory
val rData = Reg(Vec(4,UInt(8.W)))
// holds the bytes that must be read according to the activeByteLane
val data = Reg(Vec(4,UInt(8.W)))
val data = Wire(Vec(4,UInt(8.W)))

when(io.req.fire() && io.req.bits.isWrite){

Expand Down Expand Up @@ -65,4 +65,68 @@ class DummyMemController/*(programFile: Option[String])*/(implicit val config: B



}
class BlockRamWithMasking[A <: AbstrRequest, B <: AbstrResponse]
(gen: A, gen1: B, rows: Int) extends Module {


val io = IO(new Bundle {
val req = Flipped(Decoupled(gen))
val rsp = Decoupled(gen1)
})

// holds the data in byte vectors to be written in memory
val wdata = Wire(Vec(4, UInt(8.W)))
// holds the data in byte vectors read from memory
val rdata = Wire(Vec(4, UInt(8.W)))
// holds the mask signals to be used for byte masking in memory
val mask = Wire(Vec(4, Bool()))
// holds the bytes that must be read according to the activeByteLane
val data = Wire(Vec(4, UInt(8.W)))

wdata(0) := io.req.bits.dataRequest(7,0)
wdata(1) := io.req.bits.dataRequest(15,8)
wdata(2) := io.req.bits.dataRequest(23,16)
wdata(3) := io.req.bits.dataRequest(31,24)


// connecting the mask bits with activeByteLane bits
val byteLane = io.req.bits.activeByteLane.asBools()
mask zip byteLane map {case(m, b) =>
m := b
}


// the register that sends valid along with the data read from memory
// a register is used so that it synchronizes along with the data that comes after one cycle
val validReg = RegInit(false.B)
io.rsp.valid := validReg
io.rsp.bits.error := false.B // assuming memory controller would never return an error
io.req.ready := true.B // assuming we are always ready to accept requests from device

val mem = SyncReadMem(rows, Vec(4, UInt((32/4).W)))

when(io.req.fire() && !io.req.bits.isWrite) {
// READ
rdata := mem.read(io.req.bits.addrRequest)
validReg := true.B
} .elsewhen(io.req.fire() && io.req.bits.isWrite) {
// WRITE
mem.write(io.req.bits.addrRequest, wdata, mask)
validReg := true.B
rdata map (_ := DontCare)
} .otherwise {
validReg := false.B
rdata map (_ := DontCare)
}

/** return only those bytes which are enabled by mask
* else return 0s*/
data := mask zip rdata map {case (b: Bool, i: UInt) =>
Mux(b === true.B, i, 0.U)
}

io.rsp.bits.dataResponse := Cat(data(3), data(2) ,data(1), data(0))


}
21 changes: 17 additions & 4 deletions src/main/scala/caravan/bus/tilelink/Harness.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package caravan.bus.tilelink
import caravan.bus.common.{AddressMap, BusDecoder, DeviceAdapter, Switch1toN, DummyMemController}
import caravan.bus.common.{AddressMap, BusDecoder, DeviceAdapter, Switch1toN, DummyMemController,BlockRamWithMasking}
import chisel3._
import chisel3.experimental.ChiselEnum
import chisel3.stage.ChiselStage
Expand All @@ -14,6 +14,12 @@ class TilelinkHarness/*(programFile: Option[String])*/(implicit val config: Tile
val dataReq = Input(UInt((config.w * 8).W))
val byteLane = Input(UInt(config.w.W))
val isWrite = Input(Bool())
val isArithmetic = if(config.uh) Some(Input(Bool())) else None
val isLogical = if(config.uh) Some(Input(Bool())) else None
val isIntent = if(config.uh) Some(Input(Bool())) else None
val param = if(config.uh) Some(Input(UInt(3.W))) else None
val size = if (config.uh) Some(Input(UInt(config.z.W))) else None


val validResp = Output(Bool())
val dataResp = Output(UInt(32.W))
Expand All @@ -25,7 +31,7 @@ class TilelinkHarness/*(programFile: Option[String])*/(implicit val config: Tile

val tlHost = Module(new TilelinkHost())
val tlSlave = Module(new TilelinkDevice())
val memCtrl = Module(new DummyMemController())
val memCtrl = Module(new BlockRamWithMasking(new TLRequest(),new TLResponse(),1024))

tlHost.io.rspOut.ready := true.B // IP always ready to accept data from wb host

Expand All @@ -35,15 +41,22 @@ class TilelinkHarness/*(programFile: Option[String])*/(implicit val config: Tile
//tlHost.io.reqIn.valid := Mux(tlHost.io.reqIn.ready, io.valid, false.B)
tlHost.io.reqIn.valid := io.valid
tlHost.io.reqIn.bits.addrRequest := io.addrReq
tlHost.io.reqIn.bits.dataRequest := io.dataReq
tlHost.io.reqIn.bits.dataRequest := io.dataReq.asUInt
tlHost.io.reqIn.bits.activeByteLane := io.byteLane
tlHost.io.reqIn.bits.isWrite := io.isWrite

if (config.uh){
tlHost.io.reqIn.bits.isArithmetic.get := io.isArithmetic.get
tlHost.io.reqIn.bits.isLogical.get := io.isLogical.get
tlHost.io.reqIn.bits.isIntent.get := io.isIntent.get
tlHost.io.reqIn.bits.param.get := io.param.get
tlHost.io.reqIn.bits.size.get := io.size.get
}


tlSlave.io.reqOut <> memCtrl.io.req
tlSlave.io.rspIn <> memCtrl.io.rsp


io.dataResp := tlHost.io.rspOut.bits.dataResponse
io.validResp := tlHost.io.rspOut.valid
// io.ackResp := tlHost.io.rspOut.bits.ackWrite
Expand Down
26 changes: 26 additions & 0 deletions src/main/scala/caravan/bus/tilelink/MemIO.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package caravan.bus.tilelink

import chisel3._

/**
* This abstract class provides a template for other protocols to implement the transaction wires.
* This is used as a template for e.g when the core wants to communicate with the memory or with the peripheral registers.
* It will set these signals up in order to talk to the Host adapter of the relevant bus protocol
*/
class MemRequestIO(implicit val config: TilelinkConfig) extends Bundle {
val addrRequest: UInt = Input(UInt(32.W))
val dataRequest: UInt = Input(UInt(32.W))
val activeByteLane: UInt = Input(UInt(4.W))
val isWrite: Bool = Input(Bool())
val isArithmetic = if(config.uh) Some(Bool()) else None
val isLogical = if(config.uh) Some(Bool()) else None
val isIntent = if(config.uh) Some(Bool()) else None
val param = if(config.uh) Some(UInt(3.W)) else None
val size = if (config.uh) Some(UInt(config.z.W)) else None

}

class MemResponseIO(implicit val config: TilelinkConfig) extends Bundle {
val dataResponse: UInt = Input(UInt((config.w * 8).W))
val error : Bool = Bool()
}
Loading

0 comments on commit 6e1da05

Please sign in to comment.