diff --git a/Makefile b/Makefile index 9ce91d7..714345e 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/doc/system_architecture.md b/doc/system_architecture.md index bcc502c..79c87ff 100644 --- a/doc/system_architecture.md +++ b/doc/system_architecture.md @@ -4,7 +4,7 @@ Manta works by having a set of configurable cores daisy-chained together across ## Bus -This daisy-chaining is done to make place-and-route as easy as possible - the critical timing path only exists between adjacent cores, instead of rouing back to some central core in a hub-and-spoke arrangement. This relaxed routing helps designs that span multiple clock domains and require BRAMs placed on the edges of clock domains for CDC. +This daisy-chaining is done to make place-and-route as easy as possible - the critical timing path only exists between adjacent cores, instead of rouing back to some central core in a hub-and-spoke arrangement. This relaxed routing helps designs that span multiple clock domains and require BRAMs placed on the edges of clock domains for CDC. ## Memory @@ -12,11 +12,11 @@ The memory is built of 16-bit registers living on a 16-bit address bus. Address ## Read/Write Transactions -As you'd expect, reading from some address will elicit a response from the FGPA. However, writing to some address __will not__. If you want to verify that the data you wrote to some location is valid, read from it after the write. This is done to keep state machines simple and interfaces fast. +As you'd expect, reading from some address will elicit a response from the FGPA. However, writing to some address __will not__. If you want to verify that the data you wrote to some location is valid, read from it after the write. This is done to keep state machines simple and interfaces fast. Data moves between the host computer and the FPGA over UART. UART's just an interface though, so the choice of what data to send is arbitrary. Manta encodes data exchanged between devices as messages, which are ASCII text in the following format: -```[preamble] [address] [data (optional)] [EOL]``` +```[preamble] [address] [data (optional)] [EOL]``` - The __preamble__ is just the character `M`, encoded as ASCII. @@ -46,7 +46,7 @@ The Python API has two main purposes: to generate the Verilog required to instan ### Loading configuration -Let's use the following configuration as an example: +Let's use the following configuration as an example: ```yaml @@ -68,19 +68,19 @@ cores: my_logic_analyzer: type: logic_analyzer sample_depth: 4096 - + probes: larry: 1 curly: 1 moe: 1 shemp: 4 - + triggers: - larry && curly && ~moe my_lut_ram: - type: lut_ram - size: 64 + type: lut_ram + size: 64 uart: port: "/dev/tty.usbserial-2102926963071" @@ -94,13 +94,13 @@ For each core in the config file, an instance of the corresponding Python object my_logic_analyzer: type: logic_analyzer sample_depth: 4096 - + probes: larry: 1 curly: 1 moe: 1 shemp: 4 - + triggers: - larry && curly && ~moe ``` @@ -156,4 +156,12 @@ Once manta's been generated, included in your project, and built, the Python API ### Block Diagram
- \ No newline at end of file + + +Nominal interaction with the logic analyzer core should be: +- Check state - if it's in anything other than idle, request to stop the existing capture +- Once state's in IDLE, go ahead and configure trigger positions and conditions and request start. Also set it back down to zero. +- Wait for state to be in captured. If you want a timeout, then pulse stop_request once the timeout has expired. +- Read out contents from memory +- Pulse stop_request to end the capture and return the state back to IDLE + diff --git a/src/manta/__init__.py b/src/manta/__init__.py index 08e9d6b..828c699 100644 --- a/src/manta/__init__.py +++ b/src/manta/__init__.py @@ -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): diff --git a/src/manta/logic_analyzer_controller.v b/src/manta/logic_analyzer_controller.v index 4443d85..492014a 100644 --- a/src/manta/logic_analyzer_controller.v +++ b/src/manta/logic_analyzer_controller.v @@ -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 diff --git a/src/manta/logic_analyzer_def_tmpl.v b/src/manta/logic_analyzer_def_tmpl.v index 2f8107c..d21d21c 100644 --- a/src/manta/logic_analyzer_def_tmpl.v +++ b/src/manta/logic_analyzer_def_tmpl.v @@ -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), diff --git a/src/manta/logic_analyzer_fsm_registers.v b/src/manta/logic_analyzer_fsm_registers.v index fc60324..3c06ad5 100644 --- a/src/manta/logic_analyzer_fsm_registers.v +++ b/src/manta/logic_analyzer_fsm_registers.v @@ -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; diff --git a/test/functional_sim/logic_analyzer_tb/gen_logic_analyzer.py b/test/functional_sim/logic_analyzer_tb/gen_logic_analyzer.py new file mode 100644 index 0000000..46bdcdb --- /dev/null +++ b/test/functional_sim/logic_analyzer_tb/gen_logic_analyzer.py @@ -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) \ No newline at end of file diff --git a/test/functional_sim/logic_analyzer_tb/logic_analyzer.v b/test/functional_sim/logic_analyzer_tb/logic_analyzer.v index 48e5e77..a41ea4c 100644 --- a/test/functional_sim/logic_analyzer_tb/logic_analyzer.v +++ b/test/functional_sim/logic_analyzer_tb/logic_analyzer.v @@ -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 \ No newline at end of file + // 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