modify sim and generator, seems to work in simulation

This commit is contained in:
Fischer Moseley 2023-04-14 16:44:16 -04:00
parent 518f49cc29
commit a2ad90a66a
12 changed files with 716 additions and 509 deletions

View File

@ -44,11 +44,10 @@ io_core_tb:
rm sim.out
logic_analyzer_tb:
cd test/functional_sim/logic_analyzer_tb && python3 gen_logic_analyzer.py
iverilog -g2012 -o sim.out -y src/manta \
test/functional_sim/logic_analyzer_tb/logic_analyzer_tb.sv \
test/functional_sim/logic_analyzer_tb/logic_analyzer.v \
test/functional_sim/logic_analyzer_tb/sample_mem.v \
test/functional_sim/logic_analyzer_tb/trigger_block.v
test/functional_sim/logic_analyzer_tb/logic_analyzer.v
vvp sim.out
rm sim.out

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,5 @@
import pkgutil
import math
from sys import argv
import os
from datetime import datetime
@ -428,14 +429,14 @@ class LogicAnalyzerCore:
self.triggers = config["triggers"]
# compute addresses
# - need 3 addresses for configuration (state, current_loc, trigger_loc)
# and 2 address for each trigger (operation and argument)
# compute base addresses
self.fsm_base_addr = self.base_addr
self.trigger_block_base_addr = self.fsm_base_addr + 3
self.sample_mem_base_addr = self.trigger_block_base_addr + (2*len(self.probes))
self.max_addr = self.sample_mem_base_addr + self.sample_depth
self.trigger_block_base_addr = self.fsm_base_addr + 6
self.total_probe_width = sum(self.probes.values())
n_brams = math.ceil(self.total_probe_width / 16)
self.block_memory_base_addr = self.trigger_block_base_addr + (2*len(self.probes))
self.max_addr = self.block_memory_base_addr + (n_brams * self.sample_depth)
def hdl_inst(self):
la_inst = VerilogManipulator("logic_analyzer_inst_tmpl.v")
@ -514,29 +515,6 @@ class LogicAnalyzerCore:
return trigger_block.get_hdl()
def gen_sample_mem_def(self):
sample_mem = VerilogManipulator("sample_mem_def_tmpl.v")
# add probe ports to module declaration
# - these are the ports that belong to the logic analyzer, but
# need to be included in the trigger_block module declaration
probe_ports = sample_mem.net_dec(self.probes, "input wire", trailing_comma=True)
sample_mem.sub(probe_ports, "/* PROBE_PORTS */")
# concatenate probes to BRAM input
total_probe_width = sum([width for name, width in self.probes.items()])
if total_probe_width > 16:
# TODO: implement > 16 bit addressing
raise NotImplementedError("ummm i'm getting around to it calm down calm down")
zero_pad_width = 16 - total_probe_width
concat = ", ".join([name for name in self.probes])
concat = f"{{{zero_pad_width}'b0, {concat}}}"
sample_mem.sub(concat, "/* CONCAT */")
return sample_mem.get_hdl()
def gen_logic_analyzer_def(self):
la = VerilogManipulator("logic_analyzer_def_tmpl.v")
@ -547,7 +525,7 @@ class LogicAnalyzerCore:
# assign base addresses to the FSM, trigger block, and sample mem
la.sub(self.fsm_base_addr, "/* FSM_BASE_ADDR */")
la.sub(self.trigger_block_base_addr, "/* TRIGGER_BLOCK_BASE_ADDR */")
la.sub(self.sample_mem_base_addr, "/* SAMPLE_MEM_BASE_ADDR */")
la.sub(self.block_memory_base_addr, "/* BLOCK_MEMORY_BASE_ADDR */")
# set sample depth
la.sub(self.sample_depth, "/* SAMPLE_DEPTH */")
@ -555,21 +533,25 @@ class LogicAnalyzerCore:
# set probe ports for the trigger block and sample mem
probe_ports = la.net_conn(self.probes, trailing_comma=True)
la.sub(probe_ports, "/* TRIGGER_BLOCK_PROBE_PORTS */")
la.sub(probe_ports, "/* SAMPLE_MEM_PROBE_PORTS */")
la.sub(self.total_probe_width, "/* TOTAL_PROBE_WIDTH */")
probes_concat = '{' + ', '.join(self.probes.keys()) + '}'
la.sub(probes_concat, "/* PROBES_CONCAT */")
return la.get_hdl()
def hdl_def(self):
# Return an autogenerated verilog module definition for the core.
# load source files
la_fsm = VerilogManipulator("la_fsm.v").get_hdl()
dual_port_bram = VerilogManipulator("dual_port_bram.v").get_hdl()
trigger = VerilogManipulator("trigger.v").get_hdl()
trigger_block = self.gen_trigger_block_def()
sample_mem = self.gen_sample_mem_def()
logic_analyzer = self.gen_logic_analyzer_def()
hdl = self.gen_logic_analyzer_def() + "\n"
hdl += VerilogManipulator("logic_analyzer_controller.v").get_hdl() + "\n"
hdl += VerilogManipulator("block_memory.v").get_hdl() + "\n"
hdl += VerilogManipulator("dual_port_bram.v").get_hdl() + "\n"
hdl += self.gen_trigger_block_def() + "\n"
hdl += VerilogManipulator("trigger.v").get_hdl() + "\n"
return logic_analyzer + la_fsm + sample_mem + dual_port_bram + trigger_block + trigger
return hdl
def hdl_top_level_ports(self):
# the probes that we want as ports on the top-level manta module
@ -677,9 +659,8 @@ class BlockMemoryCore:
assert isinstance(config["width"], int), "Block Memory core must have integer width."
self.width = config["width"]
from math import ceil, floor, log2
self.addr_width = ceil(log2(self.depth))
self.n_brams = ceil(self.width / 16)
self.addr_width = math.ceil(math.log2(self.depth))
self.n_brams = math.ceil(self.width / 16)
self.max_addr = self.base_addr + (self.depth * self.n_brams)
def hdl_inst(self):

View File

@ -10,7 +10,7 @@ module logic_analyzer_controller (
output reg signed [15:0] current_loc,
input wire request_start,
input wire request_stop,
output reg read_pointer,
output reg [ADDR_WIDTH-1:0] read_pointer,
// from trigger block
input wire trig,
@ -20,8 +20,11 @@ module logic_analyzer_controller (
output bram_we
);
parameter DEPTH = 0;
localparam ADDR_WIDTH = $clog2(DEPTH);
assign bram_addr = write_pointer;
assign bram_we = acquire;
parameter SAMPLE_DEPTH= 0;
localparam ADDR_WIDTH = $clog2(SAMPLE_DEPTH);
/* ----- FSM ----- */
localparam IDLE = 0;
@ -53,17 +56,18 @@ module logic_analyzer_controller (
// go into MOVE_TO_POSITION or IN_POSITION. that's for
// the morning
state <= MOVE_TO_POSITION;
clear <= 0;
end
end
if(state == MOVE_TO_POSITION) begin
else if(state == MOVE_TO_POSITION) begin
acquire <= 1;
current_loc <= current_loc + 1;
if(current_loc == trigger_loc) state <= IN_POSITION
if(current_loc == trigger_loc) state <= IN_POSITION;
end
if(state == IN_POSITION) begin
else if(state == IN_POSITION) begin
acquire <= 1;
pop <= 1;
@ -71,25 +75,30 @@ module logic_analyzer_controller (
if(trig) state <= CAPTURING;
end
if(state == CAPTURING) begin
if(size == DEPTH) state <= CAPTURED;
else if(state == CAPTURING) begin
acquire <= 1;
if(size == SAMPLE_DEPTH) begin
state <= CAPTURED;
acquire <= 0;
end
end
if(state == CAPTURED) begin
else if(state == CAPTURED) begin
// actually nothing to do here doooodeeedoooo
end
if(request_stop && ~prev_request_stop) state <= IDLE;
else if(request_stop && ~prev_request_stop) state <= IDLE;
else state <= IDLE;
end
// fifo
/* ----- FIFO ----- */
reg acquire;
reg pop;
reg [ADDR_WIDTH:0] size,
reg clear,
reg [ADDR_WIDTH:0] size;
reg clear;
reg [ADDR_WIDTH:0] write_pointer = 0;
initial read_pointer = 0;
@ -99,7 +108,7 @@ module logic_analyzer_controller (
always @(posedge clk) begin
if (clear) read_pointer <= write_pointer;
if (acquire && size < DEPTH) write_pointer <= write_pointer + 1'd1;
if (acquire && size < SAMPLE_DEPTH) write_pointer <= write_pointer + 1'd1;
if (pop && size > 0) read_pointer <= read_pointer + 1'd1;
end
endmodule

View File

@ -21,8 +21,7 @@ module logic_analyzer (
output reg rw_o,
output reg valid_o
);
parameter SAMPLE_DEPTH = 0;
localparam SAMPLE_DEPTH = /* SAMPLE_DEPTH */;
localparam ADDR_WIDTH = $clog2(SAMPLE_DEPTH);
reg [3:0] state;
@ -37,11 +36,11 @@ module logic_analyzer (
reg [ADDR_WIDTH-1:0] bram_addr;
reg bram_we;
localparam TOTAL_PROBE_WIDTH = 0;
localparam TOTAL_PROBE_WIDTH = /* TOTAL_PROBE_WIDTH */;
reg [TOTAL_PROBE_WIDTH-1:0] probes_concat;
assign probes_concat = /* PROBES_CONCAT */;
logic_analyzer_controller la_controller (
logic_analyzer_controller #(.SAMPLE_DEPTH(SAMPLE_DEPTH)) la_controller (
.clk(clk),
// from register file
@ -61,7 +60,8 @@ module logic_analyzer (
);
logic_analyzer_fsm_registers #(
.BASE_ADDR(/* FSM_BASE_ADDR */)
.BASE_ADDR(/* FSM_BASE_ADDR */),
.SAMPLE_DEPTH(SAMPLE_DEPTH)
) fsm_registers (
.clk(clk),
@ -90,8 +90,6 @@ module logic_analyzer (
reg fsm_reg_trig_blk_rw;
reg fsm_reg_trig_blk_valid;
reg trig;
// trigger block
trigger_block #(.BASE_ADDR(/* TRIGGER_BLOCK_BASE_ADDR */)) trig_blk (
.clk(clk),
@ -120,9 +118,9 @@ module logic_analyzer (
// sample memory
block_memory #(
.BASE_ADDR(/* SAMPLE_MEM_BASE_ADDR */),
.WIDTH(),
.DEPTH(/* SAMPLE_DEPTH */)
.BASE_ADDR(/* BLOCK_MEMORY_BASE_ADDR */),
.WIDTH(TOTAL_PROBE_WIDTH),
.DEPTH(SAMPLE_DEPTH)
) block_mem (
.clk(clk),

View File

@ -24,11 +24,17 @@ module logic_analyzer_fsm_registers(
input wire signed [15:0] current_loc,
output reg request_start,
output reg request_stop,
input wire [15:0] read_pointer
input wire [ADDR_WIDTH-1:0] read_pointer
);
initial trigger_loc = 0;
initial request_start = 0;
initial request_stop = 0;
parameter BASE_ADDR = 0;
localparam MAX_ADDR = BASE_ADDR + 5;
parameter SAMPLE_DEPTH = 0;
parameter ADDR_WIDTH = $clog2(SAMPLE_DEPTH);
always @(posedge clk) begin
addr_o <= addr_i;

View File

@ -0,0 +1,7 @@
from manta import Manta
m = Manta('manta.yaml')
la = m.my_logic_analyzer.hdl_def()
with open('logic_analyzer.v', 'w') as f:
f.write(la)

View File

@ -1,7 +1,4 @@
`default_nettype none
`timescale 1ns/1ps
module logic_analyzer(
module logic_analyzer (
input wire clk,
// probes
@ -24,19 +21,49 @@ module logic_analyzer(
output reg rw_o,
output reg valid_o
);
localparam SAMPLE_DEPTH = 128;
localparam ADDR_WIDTH = $clog2(SAMPLE_DEPTH);
parameter BASE_ADDR = 0;
parameter SAMPLE_DEPTH = 0;
reg [3:0] state;
reg signed [15:0] trigger_loc;
reg signed [15:0] current_loc;
reg request_start;
reg request_stop;
reg [ADDR_WIDTH-1:0] read_pointer;
// fsm
la_fsm #(.BASE_ADDR(BASE_ADDR), .SAMPLE_DEPTH(SAMPLE_DEPTH)) fsm (
reg trig;
reg [ADDR_WIDTH-1:0] bram_addr;
reg bram_we;
localparam TOTAL_PROBE_WIDTH = 7;
reg [TOTAL_PROBE_WIDTH-1:0] probes_concat;
assign probes_concat = {larry, curly, moe, shemp};
logic_analyzer_controller #(.SAMPLE_DEPTH(SAMPLE_DEPTH)) la_controller (
.clk(clk),
// from register file
.state(state),
.trigger_loc(trigger_loc),
.current_loc(current_loc),
.request_start(request_start),
.request_stop(request_stop),
.read_pointer(read_pointer),
// from trigger block
.trig(trig),
.fifo_size(fifo_size),
.fifo_acquire(fifo_acquire),
.fifo_pop(fifo_pop),
.fifo_clear(fifo_clear),
// from block memory user port
.bram_addr(bram_addr),
.bram_we(bram_we)
);
logic_analyzer_fsm_registers #(
.BASE_ADDR(0),
.SAMPLE_DEPTH(SAMPLE_DEPTH)
) fsm_registers (
.clk(clk),
.addr_i(addr_i),
.wdata_i(wdata_i),
@ -44,27 +71,27 @@ module logic_analyzer(
.rw_i(rw_i),
.valid_i(valid_i),
.addr_o(fsm_trig_blk_addr),
.wdata_o(fsm_trig_blk_wdata),
.rdata_o(fsm_trig_blk_rdata),
.rw_o(fsm_trig_blk_rw),
.valid_o(fsm_trig_blk_valid));
.addr_o(fsm_reg_trig_blk_addr),
.wdata_o(fsm_reg_trig_blk_wdata),
.rdata_o(fsm_reg_trig_blk_rdata),
.rw_o(fsm_reg_trig_blk_rw),
.valid_o(fsm_reg_trig_blk_valid),
reg [15:0] fsm_trig_blk_addr;
reg [15:0] fsm_trig_blk_wdata;
reg [15:0] fsm_trig_blk_rdata;
reg fsm_trig_blk_rw;
reg fsm_trig_blk_valid;
reg trig;
reg [$clog2(SAMPLE_DEPTH):0] fifo_size;
reg fifo_acquire;
reg fifo_pop;
reg fifo_clear;
.state(state),
.trigger_loc(trigger_loc),
.current_loc(current_loc),
.request_start(request_start),
.request_stop(request_stop),
.read_pointer(read_pointer));
reg [15:0] fsm_reg_trig_blk_addr;
reg [15:0] fsm_reg_trig_blk_wdata;
reg [15:0] fsm_reg_trig_blk_rdata;
reg fsm_reg_trig_blk_rw;
reg fsm_reg_trig_blk_valid;
// trigger block
trigger_block #(.BASE_ADDR(BASE_ADDR + 3)) trig_blk(
trigger_block #(.BASE_ADDR(6)) trig_blk (
.clk(clk),
.larry(larry),
@ -74,53 +101,497 @@ module logic_analyzer(
.trig(trig),
.addr_i(fsm_trig_blk_addr),
.wdata_i(fsm_trig_blk_wdata),
.rdata_i(fsm_trig_blk_rdata),
.rw_i(fsm_trig_blk_rw),
.valid_i(fsm_trig_blk_valid),
.addr_i(fsm_reg_trig_blk_addr),
.wdata_i(fsm_reg_trig_blk_wdata),
.rdata_i(fsm_reg_trig_blk_rdata),
.rw_i(fsm_reg_trig_blk_rw),
.valid_i(fsm_reg_trig_blk_valid),
.addr_o(trig_blk_sample_mem_addr),
.wdata_o(trig_blk_sample_mem_wdata),
.rdata_o(trig_blk_sample_mem_rdata),
.rw_o(trig_blk_sample_mem_rw),
.valid_o(trig_blk_sample_mem_valid));
.addr_o(trig_blk_block_mem_addr),
.wdata_o(trig_blk_block_mem_wdata),
.rdata_o(trig_blk_block_mem_rdata),
.rw_o(trig_blk_block_mem_rw),
.valid_o(trig_blk_block_mem_valid));
reg [15:0] trig_blk_sample_mem_addr;
reg [15:0] trig_blk_sample_mem_wdata;
reg [15:0] trig_blk_sample_mem_rdata;
reg trig_blk_sample_mem_rw;
reg trig_blk_sample_mem_valid;
reg [15:0] trig_blk_block_mem_addr;
reg [15:0] trig_blk_block_mem_wdata;
reg [15:0] trig_blk_block_mem_rdata;
reg trig_blk_block_mem_rw;
reg trig_blk_block_mem_valid;
// sample memory
sample_mem #(.BASE_ADDR(BASE_ADDR + 11), .SAMPLE_DEPTH(SAMPLE_DEPTH)) sample_mem(
block_memory #(
.BASE_ADDR(14),
.WIDTH(TOTAL_PROBE_WIDTH),
.DEPTH(SAMPLE_DEPTH)
) block_mem (
.clk(clk),
// fifo
.acquire(fifo_acquire),
.pop(fifo_pop),
.size(fifo_size),
.clear(fifo_clear),
// probes
.larry(larry),
.curly(curly),
.moe(moe),
.shemp(shemp),
// input port
.addr_i(trig_blk_sample_mem_addr),
.wdata_i(trig_blk_sample_mem_wdata),
.rdata_i(trig_blk_sample_mem_rdata),
.rw_i(trig_blk_sample_mem_rw),
.valid_i(trig_blk_sample_mem_valid),
.addr_i(trig_blk_block_mem_addr),
.wdata_i(trig_blk_block_mem_wdata),
.rdata_i(trig_blk_block_mem_rdata),
.rw_i(trig_blk_block_mem_rw),
.valid_i(trig_blk_block_mem_valid),
// output port
.addr_o(addr_o),
.wdata_o(wdata_o),
.rdata_o(rdata_o),
.rw_o(rw_o),
.valid_o(valid_o));
endmodule
.valid_o(valid_o),
`default_nettype wire
// BRAM itself
.user_clk(clk),
.user_addr(bram_addr),
.user_din(probes_concat),
.user_dout(),
.user_we(bram_we));
endmodule
module logic_analyzer_controller (
input wire clk,
// from register file
output reg [3:0] state,
input wire signed [15:0] trigger_loc,
output reg signed [15:0] current_loc,
input wire request_start,
input wire request_stop,
output reg [ADDR_WIDTH-1:0] read_pointer,
// from trigger block
input wire trig,
// block memory user port
output [ADDR_WIDTH-1:0] bram_addr,
output bram_we
);
assign bram_addr = write_pointer;
assign bram_we = acquire;
parameter SAMPLE_DEPTH= 0;
localparam ADDR_WIDTH = $clog2(SAMPLE_DEPTH);
/* ----- FSM ----- */
localparam IDLE = 0;
localparam MOVE_TO_POSITION = 1;
localparam IN_POSITION = 2;
localparam CAPTURING = 3;
localparam CAPTURED = 4;
initial state = IDLE;
initial current_loc = 0;
// rising edge detection for start/stop requests
reg prev_request_start;
always @(posedge clk) prev_request_start <= request_start;
reg prev_request_stop;
always @(posedge clk) prev_request_stop <= request_stop;
always @(posedge clk) begin
// don't do anything to the FIFO unless told to
acquire <= 0;
pop <= 0;
if(state == IDLE) begin
clear <= 1;
if(request_start && ~prev_request_start) begin
// TODO: figure out what determines whether or not we
// go into MOVE_TO_POSITION or IN_POSITION. that's for
// the morning
state <= MOVE_TO_POSITION;
clear <= 0;
end
end
else if(state == MOVE_TO_POSITION) begin
acquire <= 1;
current_loc <= current_loc + 1;
if(current_loc == trigger_loc) state <= IN_POSITION;
end
else if(state == IN_POSITION) begin
acquire <= 1;
pop <= 1;
if(trig) pop <= 0;
if(trig) state <= CAPTURING;
end
else if(state == CAPTURING) begin
acquire <= 1;
if(size == SAMPLE_DEPTH) begin
state <= CAPTURED;
acquire <= 0;
end
end
else if(state == CAPTURED) begin
// actually nothing to do here doooodeeedoooo
end
else if(request_stop && ~prev_request_stop) state <= IDLE;
else state <= IDLE;
end
/* ----- FIFO ----- */
reg acquire;
reg pop;
reg [ADDR_WIDTH:0] size;
reg clear;
reg [ADDR_WIDTH:0] write_pointer = 0;
initial read_pointer = 0;
initial write_pointer = 0;
assign size = write_pointer - read_pointer;
always @(posedge clk) begin
if (clear) read_pointer <= write_pointer;
if (acquire && size < SAMPLE_DEPTH) write_pointer <= write_pointer + 1'd1;
if (pop && size > 0) read_pointer <= read_pointer + 1'd1;
end
endmodule
module block_memory (
input wire clk,
// input port
input wire [15:0] addr_i,
input wire [15:0] wdata_i,
input wire [15:0] rdata_i,
input wire rw_i,
input wire valid_i,
// output port
output reg [15:0] addr_o,
output reg [15:0] wdata_o,
output reg [15:0] rdata_o,
output reg rw_o,
output reg valid_o,
// BRAM itself
input wire user_clk,
input wire [ADDR_WIDTH-1:0] user_addr,
input wire [WIDTH-1:0] user_din,
output reg [WIDTH-1:0] user_dout,
input wire user_we);
parameter BASE_ADDR = 0;
parameter WIDTH = 0;
parameter DEPTH = 0;
localparam ADDR_WIDTH = $clog2(DEPTH);
// ugly typecasting, but just computes ceil(WIDTH / 16)
localparam N_BRAMS = int'($ceil(real'(WIDTH) / 16.0));
localparam MAX_ADDR = BASE_ADDR + (DEPTH * N_BRAMS);
// Port A of BRAMs
reg [N_BRAMS-1:0][ADDR_WIDTH-1:0] addra = 0;
reg [N_BRAMS-1:0][15:0] dina = 0;
reg [N_BRAMS-1:0][15:0] douta;
reg [N_BRAMS-1:0] wea = 0;
// Port B of BRAMs
reg [N_BRAMS-1:0][15:0] dinb;
reg [N_BRAMS-1:0][15:0] doutb;
assign dinb = user_din;
// kind of a hack to part select from a 2d array that's been flattened to 1d
reg [(N_BRAMS*16)-1:0] doutb_flattened;
assign doutb_flattened = doutb;
assign user_dout = doutb_flattened[WIDTH-1:0];
// Pipelining
reg [2:0][15:0] addr_pipe = 0;
reg [2:0][15:0] wdata_pipe = 0;
reg [2:0][15:0] rdata_pipe = 0;
reg [2:0] valid_pipe = 0;
reg [2:0] rw_pipe = 0;
always @(posedge clk) begin
addr_pipe[0] <= addr_i;
wdata_pipe[0] <= wdata_i;
rdata_pipe[0] <= rdata_i;
valid_pipe[0] <= valid_i;
rw_pipe[0] <= rw_i;
addr_o <= addr_pipe[2];
wdata_o <= wdata_pipe[2];
rdata_o <= rdata_pipe[2];
valid_o <= valid_pipe[2];
rw_o <= rw_pipe[2];
for(int i=1; i<3; i=i+1) begin
addr_pipe[i] <= addr_pipe[i-1];
wdata_pipe[i] <= wdata_pipe[i-1];
rdata_pipe[i] <= rdata_pipe[i-1];
valid_pipe[i] <= valid_pipe[i-1];
rw_pipe[i] <= rw_pipe[i-1];
end
// throw BRAM operations into the front of the pipeline
wea <= 0;
if( (valid_i) && (addr_i >= BASE_ADDR) && (addr_i <= MAX_ADDR)) begin
wea[addr_i % N_BRAMS] <= rw_i;
addra[addr_i % N_BRAMS] <= (addr_i - BASE_ADDR) / N_BRAMS;
dina[addr_i % N_BRAMS] <= wdata_i;
end
// pull BRAM reads from the back of the pipeline
if( (valid_pipe[2]) && (addr_pipe[2] >= BASE_ADDR) && (addr_pipe[2] <= MAX_ADDR)) begin
rdata_o <= douta[addr_pipe[2] % N_BRAMS];
end
end
// generate the BRAMs
genvar i;
generate
for(i=0; i<N_BRAMS; i=i+1) begin
dual_port_bram #(
.RAM_WIDTH(16),
.RAM_DEPTH(DEPTH)
) bram_full_width_i (
// port A is controlled by the bus
.clka(clk),
.addra(addra[i]),
.dina(dina[i]),
.douta(douta[i]),
.wea(wea[i]),
// port B is exposed to the user
.clkb(user_clk),
.addrb(user_addr),
.dinb(dinb[i]),
.doutb(doutb[i]),
.web(user_we));
end
endgenerate
endmodule
// Xilinx True Dual Port RAM, Read First, Dual Clock
// This code implements a parameterizable true dual port memory (both ports can read and write).
// The behavior of this RAM is when data is written, the prior memory contents at the write
// address are presented on the output port. If the output data is
// not needed during writes or the last read value is desired to be retained,
// it is suggested to use a no change RAM as it is more power efficient.
// If a reset or enable is not necessary, it may be tied off or removed from the code.
// Modified from the xilinx_true_dual_port_read_first_2_clock_ram verilog language template.
module dual_port_bram #(
parameter RAM_WIDTH = 0,
parameter RAM_DEPTH = 0
) (
input wire [$clog2(RAM_DEPTH-1)-1:0] addra,
input wire [$clog2(RAM_DEPTH-1)-1:0] addrb,
input wire [RAM_WIDTH-1:0] dina,
input wire [RAM_WIDTH-1:0] dinb,
input wire clka,
input wire clkb,
input wire wea,
input wire web,
output wire [RAM_WIDTH-1:0] douta,
output wire [RAM_WIDTH-1:0] doutb
);
// The following code either initializes the memory values to a specified file or to all zeros to match hardware
generate
integer i;
initial begin
for (i = 0; i < RAM_DEPTH; i = i + 1)
BRAM[i] = {RAM_WIDTH{1'b0}};
end
endgenerate
reg [RAM_WIDTH-1:0] BRAM [RAM_DEPTH-1:0];
reg [RAM_WIDTH-1:0] ram_data_a = {RAM_WIDTH{1'b0}};
reg [RAM_WIDTH-1:0] ram_data_b = {RAM_WIDTH{1'b0}};
always @(posedge clka) begin
if (wea) BRAM[addra] <= dina;
ram_data_a <= BRAM[addra];
end
always @(posedge clkb) begin
if (web) BRAM[addrb] <= dinb;
ram_data_b <= BRAM[addrb];
end
// Add a 2 clock cycle read latency to improve clock-to-out timing
reg [RAM_WIDTH-1:0] douta_reg = {RAM_WIDTH{1'b0}};
reg [RAM_WIDTH-1:0] doutb_reg = {RAM_WIDTH{1'b0}};
always @(posedge clka) douta_reg <= ram_data_a;
always @(posedge clkb) doutb_reg <= ram_data_b;
assign douta = douta_reg;
assign doutb = doutb_reg;
endmodule
module trigger_block (
input wire clk,
// probes
input wire larry,
input wire curly,
input wire moe,
input wire [3:0] shemp,
// trigger
output reg trig,
// input port
input wire [15:0] addr_i,
input wire [15:0] wdata_i,
input wire [15:0] rdata_i,
input wire rw_i,
input wire valid_i,
// output port
output reg [15:0] addr_o,
output reg [15:0] wdata_o,
output reg [15:0] rdata_o,
output reg rw_o,
output reg valid_o);
parameter BASE_ADDR = 0;
localparam MAX_ADDR = 7;
// trigger configuration registers
// - each probe gets an operation and a compare register
// - at the end we OR them all together. along with any custom probes the user specs
reg [3:0] larry_op = 0;
reg larry_arg = 0;
reg larry_trig;
trigger #(.INPUT_WIDTH(1)) larry_trigger (
.clk(clk),
.probe(larry),
.op(larry_op),
.arg(larry_arg),
.trig(larry_trig));
reg [3:0] curly_op = 0;
reg curly_arg = 0;
reg curly_trig;
trigger #(.INPUT_WIDTH(1)) curly_trigger (
.clk(clk),
.probe(curly),
.op(curly_op),
.arg(curly_arg),
.trig(curly_trig));
reg [3:0] moe_op = 0;
reg moe_arg = 0;
reg moe_trig;
trigger #(.INPUT_WIDTH(1)) moe_trigger (
.clk(clk),
.probe(moe),
.op(moe_op),
.arg(moe_arg),
.trig(moe_trig));
reg [3:0] shemp_op = 0;
reg [3:0] shemp_arg = 0;
reg shemp_trig;
trigger #(.INPUT_WIDTH(4)) shemp_trigger (
.clk(clk),
.probe(shemp),
.op(shemp_op),
.arg(shemp_arg),
.trig(shemp_trig));
assign trig = larry_trig || curly_trig || moe_trig || shemp_trig;
// perform register operations
always @(posedge clk) begin
addr_o <= addr_i;
wdata_o <= wdata_i;
rdata_o <= rdata_i;
rw_o <= rw_i;
valid_o <= valid_i;
rdata_o <= rdata_i;
if( (addr_i >= BASE_ADDR) && (addr_i <= BASE_ADDR + MAX_ADDR) ) begin
// reads
if(valid_i && !rw_i) begin
case (addr_i)
BASE_ADDR + 0: rdata_o <= larry_op;
BASE_ADDR + 1: rdata_o <= larry_arg;
BASE_ADDR + 2: rdata_o <= curly_op;
BASE_ADDR + 3: rdata_o <= curly_arg;
BASE_ADDR + 4: rdata_o <= moe_op;
BASE_ADDR + 5: rdata_o <= moe_arg;
BASE_ADDR + 6: rdata_o <= shemp_op;
BASE_ADDR + 7: rdata_o <= shemp_arg;
endcase
end
// writes
else if(valid_i && rw_i) begin
case (addr_i)
BASE_ADDR + 0: larry_op <= wdata_i;
BASE_ADDR + 1: larry_arg <= wdata_i;
BASE_ADDR + 2: curly_op <= wdata_i;
BASE_ADDR + 3: curly_arg <= wdata_i;
BASE_ADDR + 4: moe_op <= wdata_i;
BASE_ADDR + 5: moe_arg <= wdata_i;
BASE_ADDR + 6: shemp_op <= wdata_i;
BASE_ADDR + 7: shemp_arg <= wdata_i;
endcase
end
end
end
endmodule
module trigger (
input wire clk,
input wire [INPUT_WIDTH-1:0] probe,
input wire [3:0] op,
input wire [INPUT_WIDTH-1:0] arg,
output reg trig);
parameter INPUT_WIDTH = 0;
localparam DISABLE = 0;
localparam RISING = 1;
localparam FALLING = 2;
localparam CHANGING = 3;
localparam GT = 4;
localparam LT = 5;
localparam GEQ = 6;
localparam LEQ = 7;
localparam EQ = 8;
localparam NEQ = 9;
reg [INPUT_WIDTH-1:0] probe_prev = 0;
always @(posedge clk) probe_prev <= probe;
always @(*) begin
case (op)
RISING : trig = (probe > probe_prev);
FALLING : trig = (probe < probe_prev);
CHANGING : trig = (probe != probe_prev);
GT: trig = (probe > arg);
LT: trig = (probe < arg);
GEQ: trig = (probe >= arg);
LEQ: trig = (probe <= arg);
EQ: trig = (probe == arg);
NEQ: trig = (probe != arg);
default: trig = 0;
endcase
end
endmodule

View File

@ -51,13 +51,12 @@ task write_and_verify(
endtask
task read_all_reg();
string desc;
for(int i = 0; i < (logic_analyzer_tb.la.sample_mem.BASE_ADDR + logic_analyzer_tb.la.SAMPLE_DEPTH); i++) begin
for(int i = 0; i < (logic_analyzer_tb.la.block_mem.MAX_ADDR); i++) begin
if(i == logic_analyzer_tb.la.fsm.BASE_ADDR) desc = "FSM";
if(i == logic_analyzer_tb.la.fsm_registers.BASE_ADDR) desc = "FSM";
if(i == logic_analyzer_tb.la.trig_blk.BASE_ADDR) desc = "TRIG BLK";
if(i == logic_analyzer_tb.la.sample_mem.BASE_ADDR) desc = "SAMPLE MEM";
if(i == logic_analyzer_tb.la.block_mem.BASE_ADDR) desc = "SAMPLE MEM";
read_reg(i, logic_analyzer_tb.read_value, desc);
end
@ -90,7 +89,7 @@ module logic_analyzer_tb;
logic la_tb_valid;
logic_analyzer #(.BASE_ADDR(0), .SAMPLE_DEPTH(128)) la(
logic_analyzer la(
.clk(clk),
// probes
@ -141,127 +140,77 @@ module logic_analyzer_tb;
#`HCP
#(10*`CP);
/* ==== Test 1 Begin ==== */
$display("\n=== test 1: read/write to FSM registers, verify ===");
test_num = 1;
// state register
write_and_verify(0, la.fsm.IDLE, "state reg");
write_and_verify(0, la.fsm.FILLED, "state reg");
write_and_verify(0, la.fsm.IDLE, "state reg");
// /* ==== Test 2 Begin ==== */
// $display("\n=== test 2: read/write to trigger block registers, verify ===");
// test_num = 2;
// trigger_loc register
write_and_verify(1, 0, "trigger_loc reg");
write_and_verify(1, 'h69, "trigger_loc reg");
write_and_verify(1, 'h0612, "trigger_loc reg");
// // larry
// write_and_verify(3, 0, "larry_op");
// write_and_verify(3, 2, "larry_op");
// write_and_verify(3, 0, "larry_op");
// since we just moved the trigger location, the core has started moving into position
// if it's functioning correctly. this means we need to reset the position and state
// before testing the present_loc register.
// write_and_verify(4, 0, "larry_arg");
// write_and_verify(4, 1, "larry_arg");
// write_and_verify(4, 0, "larry_arg");
// write_and_verify(1, 0, "trigger_loc reg");
// write_and_verify(0, 0, "state reg");
// // curly
// write_and_verify(5, 0, "curly_op");
// write_and_verify(5, 3, "curly_op");
// write_and_verify(5, 0, "curly_op");
// // present_loc register
// write_and_verify(2, 0, "present_loc reg");
// write_and_verify(2, 0, "present_loc reg");
#(10*`CP);
// write_and_verify(6, 0, "curly_arg");
// write_and_verify(6, 1, "curly_arg");
// write_and_verify(6, 0, "curly_arg");
/* ==== Test 1 End ==== */
// // moe
// write_and_verify(7, 0, "moe_op");
// write_and_verify(7, 5, "moe_op");
// write_and_verify(7, 0, "moe_op");
// write_and_verify(8, 0, "moe_arg");
// write_and_verify(8, 1, "moe_arg");
// write_and_verify(8, 0, "moe_arg");
/* ==== Test 2 Begin ==== */
$display("\n=== test 2: read/write to trigger block registers, verify ===");
test_num = 2;
// // shemp
// write_and_verify(9, 0, "shemp_op");
// write_and_verify(9, 7, "shemp_op");
// write_and_verify(9, 0, "shemp_op");
// larry
write_and_verify(3, 0, "larry_op");
write_and_verify(3, 2, "larry_op");
write_and_verify(3, 0, "larry_op");
// write_and_verify(10, 0, "shemp_arg");
// write_and_verify(10, 7, "shemp_arg");
// write_and_verify(10, 0, "shemp_arg");
write_and_verify(4, 0, "larry_arg");
write_and_verify(4, 1, "larry_arg");
write_and_verify(4, 0, "larry_arg");
// #(10*`CP);
// curly
write_and_verify(5, 0, "curly_op");
write_and_verify(5, 3, "curly_op");
write_and_verify(5, 0, "curly_op");
write_and_verify(6, 0, "curly_arg");
write_and_verify(6, 1, "curly_arg");
write_and_verify(6, 0, "curly_arg");
// moe
write_and_verify(7, 0, "moe_op");
write_and_verify(7, 5, "moe_op");
write_and_verify(7, 0, "moe_op");
write_and_verify(8, 0, "moe_arg");
write_and_verify(8, 1, "moe_arg");
write_and_verify(8, 0, "moe_arg");
// shemp
write_and_verify(9, 0, "shemp_op");
write_and_verify(9, 7, "shemp_op");
write_and_verify(9, 0, "shemp_op");
write_and_verify(10, 0, "shemp_arg");
write_and_verify(10, 7, "shemp_arg");
write_and_verify(10, 0, "shemp_arg");
#(10*`CP);
/* ==== Test 2 End ==== */
/* ==== Test 3 Begin ==== */
$display("\n=== test 3: verify FSM doesn't move out of IDLE when not running ===");
test_num = 3;
write_and_verify(3, 8, "larry_op"); // set operation to eq
write_and_verify(4, 1, "larry_arg"); // set argument to 1
// set larry = 1, verify core doesn't trigger
$display(" -> set larry = 1");
larry = 1;
$display(" -> la core is in state 0x%h", la.fsm.state);
assert(la.fsm.state == la.fsm.IDLE) else $error("core moved outside of IDLE state when not running!");
$display(" -> wait a clock cycle");
#`CP
$display(" -> la core is in state 0x%h", la.fsm.state);
assert(la.fsm.state == la.fsm.IDLE) else $error("core moved outside of IDLE state when not running!");
$display(" -> set larry = 0");
larry = 0;
#(10*`CP);
/* ==== Test 3 End ==== */
// /* ==== Test 2 End ==== */
/* ==== Test 4 Begin ==== */
$display("\n=== test 4: verify FSM does move out of IDLE when running ===");
test_num = 4;
$display(" -> moving core to START_CAPTURE");
write_reg(0, 1, "state");
$display(" -> setting up trigger (larry == 1)");
write_reg(la.trig_blk.BASE_ADDR + 0, 8, "larry_op");
write_reg(la.trig_blk.BASE_ADDR + 1, 1, "larry_arg");
$display(" -> requesting start");
write_reg(3, 1, "request_start");
write_reg(3, 0, "request_start");
#`CP
$display(" -> set larry = 1");
larry = 1;
// read
$display(" -> la core is in state 0x%h", la.fsm.state);
$display(" -> la core is in state 0x%h", la.fsm_registers.state);
$display(" -> wait a clock cycle");
#`CP
$display(" -> la core is in state 0x%h", la.fsm.state);
$display(" -> la core is in state 0x%h", la.fsm_registers.state);
// run until the FILLED state is reached
$display(" -> wait until FILLED state is reached");
while (la.fsm.state != la.fsm.FILLED) begin
while (la.fsm_registers.state != la.la_controller.CAPTURED) begin
{larry, curly, moe, shemp} = {larry, curly, moe, shemp} + 1;
#`CP;
end
@ -272,38 +221,39 @@ module logic_analyzer_tb;
#(200*`CP);
/* ==== Test 4 End ==== */
/* ==== Test 5 Begin ==== */
$display("\n=== test 5: change trigger to fire on shemp > 3, and verify ===");
test_num = 5;
// /* ==== Test 5 Begin ==== */
// $display("\n=== test 5: change trigger to fire on shemp > 3, and verify ===");
// test_num = 5;
write_and_verify(9, 6, "shemp_op"); // set operation to GT
write_and_verify(10, 3, "shemp_arg"); // set argument to 3
// write_and_verify(9, 6, "shemp_op"); // set operation to GT
// write_and_verify(10, 3, "shemp_arg"); // set argument to 3
assert( (la.fsm.state == la.fsm.IDLE) || (la.fsm.state == la.fsm.FILLED) )
else $error("core is running when it shouldn't be!");
// assert( (la.fsm_registers.state == la.la_controller.IDLE) || (la.fsm_registers.state == la.la_controller.CAPTURED) )
// else $error("core is running when it shouldn't be!");
larry = 0;
curly = 0;
moe = 0;
shemp = 0;
// larry = 0;
// curly = 0;
// moe = 0;
// shemp = 0;
write_reg(0, la.fsm.START_CAPTURE, "state");
// // start the core
// // TODO: start the core
shemp = 4;
$display(" -> set shemp = 4");
// shemp = 4;
// $display(" -> set shemp = 4");
// run until the FILLED state is reached
$display(" -> wait until FILLED state is reached");
while (la.fsm.state != la.fsm.FILLED) begin
{larry, curly, moe, shemp} = {larry, curly, moe, shemp} + 2;
#`CP;
end
// // run until the FILLED state is reached
// $display(" -> wait until FILLED state is reached");
// while (la.fsm_registers.state != la.la_controller.CAPTURED) begin
// {larry, curly, moe, shemp} = {larry, curly, moe, shemp} + 2;
// #`CP;
// end
$display(" -> read from sample memory:");
read_all_reg();
// $display(" -> read from sample memory:");
// read_all_reg();
#(10*`CP);
/* ==== Test 5 End ==== */
// #(10*`CP);
// /* ==== Test 5 End ==== */
$finish();
end

View File

@ -0,0 +1,19 @@
---
cores:
my_logic_analyzer:
type: logic_analyzer
sample_depth: 128
probes:
larry: 1
curly: 1
moe: 1
shemp: 4
triggers:
- larry && curly && ~moe
uart:
port: "auto"
baudrate: 115200
clock_freq: 100000000

View File

@ -1,116 +0,0 @@
`default_nettype none
`timescale 1ns/1ps
module sample_mem(
input wire clk,
// fifo
input wire acquire,
input wire pop,
output logic [BRAM_ADDR_WIDTH:0] size,
input wire clear,
// probes
input wire larry,
input wire curly,
input wire moe,
input wire [3:0] shemp,
// input port
input wire [15:0] addr_i,
input wire [15:0] wdata_i,
input wire [15:0] rdata_i,
input wire rw_i,
input wire valid_i,
// output port
output reg [15:0] addr_o,
output reg [15:0] wdata_o,
output reg [15:0] rdata_o,
output reg rw_o,
output reg valid_o);
parameter BASE_ADDR = 0;
parameter SAMPLE_DEPTH = 0;
localparam BRAM_ADDR_WIDTH = $clog2(SAMPLE_DEPTH);
// bus controller
reg [BRAM_ADDR_WIDTH-1:0] bram_read_addr;
reg [15:0] bram_read_data;
always @(*) begin
// if address is valid
if ( (addr_i >= BASE_ADDR) && (addr_i <= BASE_ADDR + SAMPLE_DEPTH) ) begin
// figure out proper place to read from
// want to read from the read pointer, and then loop back around
if(read_pointer + (addr_i - BASE_ADDR) > SAMPLE_DEPTH)
bram_read_addr = read_pointer + (addr_i - BASE_ADDR) - SAMPLE_DEPTH;
else
bram_read_addr = read_pointer + (addr_i - BASE_ADDR);
end
else bram_read_addr = 0;
end
// pipeline bus to compensate for 2-cycles of delay in BRAM
reg [15:0] addr_pip;
reg [15:0] wdata_pip;
reg [15:0] rdata_pip;
reg rw_pip;
reg valid_pip;
always @(posedge clk) begin
addr_pip <= addr_i;
wdata_pip <= wdata_i;
rdata_pip <= rdata_i;
rw_pip <= rw_i;
valid_pip <= valid_i;
addr_o <= addr_pip;
wdata_o <= wdata_pip;
rdata_o <= rdata_pip;
rw_o <= rw_pip;
valid_o <= valid_pip;
if( valid_pip && !rw_pip && (addr_pip >= BASE_ADDR) && (addr_pip <= BASE_ADDR + SAMPLE_DEPTH) )
rdata_o <= bram_read_data;
end
// bram
dual_port_bram #(
.RAM_WIDTH(16),
.RAM_DEPTH(SAMPLE_DEPTH)
) bram (
// read port (controlled by bus)
.clka(clk),
.addra(bram_read_addr),
.dina(16'b0),
.wea(1'b0),
.douta(bram_read_data),
// write port (controlled by FIFO)
.clkb(clk),
.addrb(write_pointer[BRAM_ADDR_WIDTH-1:0]),
.dinb({9'b0, larry, curly, moe, shemp}),
.web(acquire),
.doutb());
// fifo
reg [BRAM_ADDR_WIDTH:0] write_pointer = 0;
reg [BRAM_ADDR_WIDTH:0] read_pointer = 0;
assign size = write_pointer - read_pointer;
always @(posedge clk) begin
if (clear) read_pointer <= write_pointer;
if (acquire && size < SAMPLE_DEPTH) write_pointer <= write_pointer + 1'd1;
if (pop && size > 0) read_pointer <= read_pointer + 1'd1;
end
endmodule
`default_nettype wire

View File

@ -1,125 +0,0 @@
`default_nettype none
`timescale 1ns/1ps
module trigger_block(
input wire clk,
// probes
input wire larry,
input wire curly,
input wire moe,
input wire [3:0] shemp,
// trigger
output reg trig,
// input port
input wire [15:0] addr_i,
input wire [15:0] wdata_i,
input wire [15:0] rdata_i,
input wire rw_i,
input wire valid_i,
// output port
output reg [15:0] addr_o,
output reg [15:0] wdata_o,
output reg [15:0] rdata_o,
output reg rw_o,
output reg valid_o);
parameter BASE_ADDR = 0;
// trigger configuration registers
// - each probe gets an operation and a compare register
// - at the end we OR them all together. along with any custom probes the user specs
reg [3:0] larry_trigger_op = 0;
reg larry_trigger_arg = 0;
reg larry_trig;
trigger #(.INPUT_WIDTH(1)) larry_trigger(
.clk(clk),
.probe(larry),
.op(larry_trigger_op),
.arg(larry_trigger_arg),
.trig(larry_trig));
reg [3:0] curly_trigger_op = 0;
reg curly_trigger_arg = 0;
reg curly_trig;
trigger #(.INPUT_WIDTH(1)) curly_trigger(
.clk(clk),
.probe(curly),
.op(curly_trigger_op),
.arg(curly_trigger_arg),
.trig(curly_trig));
reg [3:0] moe_trigger_op = 0;
reg moe_trigger_arg = 0;
reg moe_trig;
trigger #(.INPUT_WIDTH(1)) moe_trigger(
.clk(clk),
.probe(moe),
.op(moe_trigger_op),
.arg(moe_trigger_arg),
.trig(moe_trig));
reg [3:0] shemp_trigger_op = 0;
reg [3:0] shemp_trigger_arg = 0;
reg shemp_trig;
trigger #(.INPUT_WIDTH(4)) shemp_trigger(
.clk(clk),
.probe(shemp),
.op(shemp_trigger_op),
.arg(shemp_trigger_arg),
.trig(shemp_trig));
assign trig = larry_trig || curly_trig || moe_trig || shemp_trig;
// perform register operations
always @(posedge clk) begin
addr_o <= addr_i;
wdata_o <= wdata_i;
rdata_o <= rdata_i;
rw_o <= rw_i;
valid_o <= valid_i;
rdata_o <= rdata_i;
if( (addr_i >= BASE_ADDR) && (addr_i <= BASE_ADDR + 7) ) begin
// reads
if(valid_i && !rw_i) begin
case (addr_i)
BASE_ADDR + 0: rdata_o <= larry_trigger_op;
BASE_ADDR + 1: rdata_o <= larry_trigger_arg;
BASE_ADDR + 2: rdata_o <= curly_trigger_op;
BASE_ADDR + 3: rdata_o <= curly_trigger_arg;
BASE_ADDR + 4: rdata_o <= moe_trigger_op;
BASE_ADDR + 5: rdata_o <= moe_trigger_arg;
BASE_ADDR + 6: rdata_o <= shemp_trigger_op;
BASE_ADDR + 7: rdata_o <= shemp_trigger_arg;
endcase
end
// writes
else if(valid_i && rw_i) begin
case (addr_i)
BASE_ADDR + 0: larry_trigger_op <= wdata_i;
BASE_ADDR + 1: larry_trigger_arg <= wdata_i;
BASE_ADDR + 2: curly_trigger_op <= wdata_i;
BASE_ADDR + 3: curly_trigger_arg <= wdata_i;
BASE_ADDR + 4: moe_trigger_op <= wdata_i;
BASE_ADDR + 5: moe_trigger_arg <= wdata_i;
BASE_ADDR + 6: shemp_trigger_op <= wdata_i;
BASE_ADDR + 7: shemp_trigger_arg <= wdata_i;
endcase
end
end
end
endmodule
`default_nettype wire