[attachment=0]
I've been having a blast with CPLDs in the Bus Blaster v2, CPLD breakout boards, and a few other projects. While waiting around for an appointment today I thought about the dev-board (http://http://hackaday.com/2008/12/11/how-to-programmable-logic-devices-cpld/) I did at Hack a Day (pictured above). It has a small CPLD, some SRAM, a buffer, and a PIC for interfacing, but it was a first attempt and riddled with issues. Coming at it fresh, I sketched out 'yet another logic analyzer' on a napkin as shown below.
[attachment=1]
The idea is that the CPLD drives the SRAM, counts samples, handles the trigger, and acts as the buffer between the IO, SRAM, and the PIC.
If a XC9572XL type CPLD is used, the pins are 5volt tolerant and no additional 5volt buffer is needed. It is also only $2.54 in 1s.
On the other hand, a coolrunnerII is not 5volt tolerant, but has a clock divider module that might help with frequency stuff (maybe we can make one in the 8 extra macrocells in the xc9572XL though). It has fewer cells, but is more modern, and comes in at $3.15 or so.
It really depends on how difficult the board routing is.
I have been hacking away at an old counter and trigger schematic, and also been making a new verilog version. I think Sjaak also mentioned toying with triggers in VHDL on the CPLD dev boards. Nothing solid yet, but it probably is important to have a test synthesis before committing to a design so we know if it will fit.
Wiki page with more links and notes here:
http://dangerousprototypes.com/docs/Lul ... ic_analyer (http://dangerousprototypes.com/docs/Lulu:_Yet_another_logic_analyer)
Just guessing:
Synchronous counter for SRAM:
1 macrocell per bit (?) = 13 to 18 macrocells, times two for the sample counter, more for control signals (~40+)
Buffer from SRAM to PIC or IO pins:
1-2 per IO (~16)
Trigger:
3-4 per bit (wild guess), (24-32)
Clock divider:
1 per bit (guess)(4-5)
Config registers/controls:
1 per bit (2 bytes triggers, 8bytes sample counter, etc)
No idea how accurate this is. It might be a real stretch to get a usable LA in a 72 macrocell CPLD if it does all the buffering and stuff too. I have no idea how bit the CPLD on the bitscope it, but it is super old so I can't imagine that many ;)
Made some intro eagle stuff. I like to see the physical relation of parts to know if the design will be simple or a real pain. It looks like real pain ;) however, I have done this on a single-sided hand-etched board before so it can't be too bad :)
Some design considerations:
*XC95 is the way to go. The clock divider in the coolrunner-ii isn't useful for this design, and we get a free 5volt buffer on a much cheaper chip that only needs a single supply rail
*A0 (1x) A1 (0.5x) A2 (0.25x) and A3 (0.125x) are the fastest signals on the board. They should be super duper short and straight to avoid any advanced design considerations. A0 could be 100MHz, A1 50MHz, A2 25MHz, etc. After that the data lines should be prioritized
*Don;t know if the SOIC or TSSOP rotated package is better for the RAM. TSSOP will line up better, but I think the pin arrangement of the SOIC is more logical
*going to start with 32Kx8 SRAM, a 15bit synchronous counter will be tough enough. moving to 16 (64K), 17(128K), and 18 (256K) bits will be a fun addition if 15bits works out
module CPLDIntro3LEDinverse(
SRAMPIN,
INPIN, //external pin number is assigned in the UCF file like this:
PICPIN, //
STATEPIN
);
output [7:0] PICPIN;
input [7:0] INPIN;
input STATEPIN;
inout [7:0] SRAMPIN;
wire [7:0] PICPIN;
wire [7:0] INPIN;
wire STATEPIN;
reg pcu_rnw;// direction (read=1 or 0=write)
assign SRAMPIN = (pcu_rnw) ? 1'bz : INPIN;
assign PICPIN = SRAMPIN; //(pcu_rnw)?SRAMPIN:INPIN;
always @ (STATEPIN) //start of the action section
begin
if(STATEPIN == 0) begin
pcu_rnw <= 1'b0; // output
end else begin
pcu_rnw <= 1'b1; // input
end
end
endmodule
This little bit of verilog is the switch that changes the SRAM pins from input to output, and also attaches the PIC pins to the SRAM. This is pretty expensive code, and it takes a quarter of the macrocells. Only 8 macrocells are used if the PIC pins are excluded (connected directly on the PCB instead, for example):
Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
16/72 (23%) 16/360 (5%) 0/72 (0%) 25/34 (74%) 14/216 (7%)
I'm going to look at synchronous counters next, and also diagram the desired internal logic.
Sounds like a cool project! Any reason for the use of Verilog instead of VHDL? I've got a little VHDL experience, but thinking about digging into Verilog as it seems to be a more broadly used (and according to some accounts, easier) language.
module first_counter (
clock , // Clock input of the design
reset , // active high, synchronous Reset input
enable , // Active high enable signal for counter
counter_out // 4 bit vector output of the counter
); // End of port list
//-------------Input Ports-----------------------------
input clock ;
input reset ;
input enable ;
//-------------Output Ports----------------------------
output [14:0] counter_out ;
//-------------Input ports Data Type-------------------
// By rule all the input ports should be wires
wire clock ;
wire reset ;
wire enable ;
//-------------Output Ports Data Type------------------
// Output port can be a storage element (reg) or a wire
reg [14:0] counter_out ;
//------------Code Starts Here-------------------------
// Since this counter is a positive edge trigged one,
// We trigger the below block with respect to positive
// edge of the clock.
always @ (posedge clock)
begin : COUNTER // Block Name
// At every rising edge of clock we check if reset is active
// If active, we load the counter output with 4'b0000
if (reset == 1'b1) begin
counter_out <= #1 14'b000000000000000;
end
// If enable is active, then we increment the counter
else if (enable == 1'b1) begin
counter_out <= #1 counter_out + 1;
end
end // End of Block COUNTER
endmodule // End of Module counter
Here is a 15bit syncronous counter. This drives the SRAM address signals. It is adopted from here:
http://www.asic-world.com/verilog/first1.html (http://www.asic-world.com/verilog/first1.html)
Resources consumed:
Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
15/72 (21%) 30/360 (9%) 15/72 (21%) 18/34 (53%) 53/216 (25%)
Basically, 15 more macrocells. I think it will take two of these. So already we are really close without any trigger logic.
Will look into simple trigger logic next, but in addition to that, we also need a configuration shift register, so this might get a bit tight in a 72macrocell device. Hopefully we can do some trigger and interface logic with the 25% left (if 2 x 15 bit counters).
I think though we might use only one counter and just latch the value at the trigger and be able to readit out and reset the counter, but that seems like it will take every bit as much logic in the form of shift registers to do.
it seems to be a more broadly used (and according to some accounts, easier) language.
We posted at the same time :) This is exactly why :) When I started to dig into dogsbody's Verilog core for the OLS I realized it just "made sense" to me as a C programmer. I have been able to hack it all over, and write it myself, with only a one-page cheat sheet of common references. I taught myself some VHDL too, but Verilog has been a stupid easy transition for me.
I was looking at the Verilog core as well and thought the same thing. It did look very similar to VHDL. I'm planning on hacking my way around the OLS core and picking it up along the way, and perhaps get a CPLD breakout/Papilio One or something.
Anyway, good luck getting everything into the 72 cells!
Thanks! I'll see how much I can fit. I want to stick with the 72 macrocell version because it is so amazingly cheap. It is a long standing goal to make a clone of the bitscope design:
http://www.bitscope.com/design/ (http://www.bitscope.com/design/)
If you look at the hack a day article for this design, the download is called bitclonev1.zip because my goal wa to make a LA out of that board ;)
A plan for the verilog in the CPLD
So after getting a feel for the macrocell requirements, it's obvious this has to be scaled way back. The nice thing is it can probably fit in a 44 or 64 pin CPLD for even more cost savings.
There's no way we can waste 8-16 macrocells on switching the data lines within the CPLD. The data will need a (74LVC573) buffer that connects to the SRAM, CPLD, and the PIC. That'll be great to route!
That leaves the essentials:
*15bit bi-directional synchronous counter (need to be able to pause and down-count for read-back)
*15 bit down counter (need to be settable for the pre-post trigger sample count)
*Trigger with single-level edge detect or don't care settings
*Clock divider
*simple shift-register interface to set the down-counter, trigger, and clock divider
*A way to switch from clock up counts to PIC-based down count for the SRAM address counter
That will probably be pressing it. Other compromises are probably ahead :) Updated diagram attached.
I'll probably work on the settable counter or trigger first.
Here's what the updated hardware will look like. 4 point connection with the buffer, SRAM, CPLD, and PIC. CPLD is mostly counter and trigger.
This is basically the same as the miniLA or its derivative CoolLA.
http://minila.sourceforge.net/ (http://minila.sourceforge.net/) or
http://coolla.freeunix.net/coolla.html (http://coolla.freeunix.net/coolla.html)
I think it would be better to use an FPGA since the CPLD based device would only allow
a very simple trigger logic. I think it is OK to build something that has a reduced feature
set to learn something,. But if you want to make it a product I think there are already to much
products on the market that could be vastly better for want of some small additional cost.
XC3S250E-4PQG208C ~ $15 at Digi-Key
XC3S500E-4PQG208C ~ $20 at Digi-Key
the first should be OK but in case you will have an upgrade path. A PQ144 would probably
also big enough but Digi-Key has only the XC3S250E-4TQG144C and not the XC3S500E-4VQG100C
Speaking of upgrades, is there a planned OLS with an XC3S500E instead of the XC3S250E? It has
66% more memory for a slightly larger price. 368640 Bit instead of 221184 Bits ( 40K to 24K)
Klaus Leiss
Hi Klaus,
Thanks for the links, I've only seen the the miniLA, not the coolLA.
My primary goal is to take on an actual problem in Verilog and solve it using code written with my own hand (eg not hacked together). I don't want to replace the OLS, I'm just having fun trying a different type of LA. I've made really slow ones (Bus Pirate, IR Toy), a medium speed one (TBD), and contributed to fast ones (OLS), but my first LA love was the bitscope design that uses a CPLD and external SRAMs on an all through-hole board:
http://www.bitscope.com/design/ (http://www.bitscope.com/design/)
I'd like to see how much of a small LA I can fit into the tiny devices that are super cheap. It won't be like the OLS, but there is already the OLS to serve those needs. The XC9572XL is only $2.40 compared to the FPGA, and a 32x8 the SRAM is only a minor additional cost.
I think you can get one of Jack's dev-board with the 500E on it, any of the LA cores can be ported to it, but I don't know the effort involved in getting the extra RAM implemented.
module sram_counter (
clock,
enable,
counter_out,
clock_enable,
down_count
);
input clock;
input enable;
input down_count;
output [14:0] counter_out;
output clock_enable;
wire clock;
wire enable;
wire down_count;
reg [14:0] counter_out;
wire clock_enable;
assign clock_enable=(enable)?!clock:1'b0; //clock enable output opposite the counter update
//SRAM counter
//controlled by:
// enable (active high)
// down_count (active high = deincrement)
always @ (posedge clock)
begin
if(enable) begin //if enabled
if(down_count) begin //if down count
counter_out <= counter_out - 1; //down count
end
else begin //if up count
counter_out <= counter_out + 1; //up count
end
end
end // End of Block COUNTER
endmodule // End of Module counter
Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
16/72 (23%) 44/360 (13%) 15/72 (21%) 19/34 (56%) 48/216 (23%)
This is the code for the SRAM counter. It is bi-directional, has an enable that will connect to the trigger, and outputs an opposite phase (can be reversed if that is wrong) clock enable output for the SRAM.
Simulation is attached. Stepping through the code showed that the counter was executing properly, but the counter value is never actually output for some reason. This is the same way the counter is done in some Xilinx examples, so I'm surprised it's not working. It will output a fixed value ok. I'll keep digging, probably my error.
Next I'll try to figure out the bug, then move on to the settable down counter for the pre/post roll counter.
(still need to dive into verilog so could be nonsense I say)
You seem to use a register instead of a wire. in vhdl a registrer is something that stays within the building block. Just my 2 cents.
looking forward to the result!
module sram_counter (
clock,
enable,
counter_out,
clock_enable,
down_count
);
input clock;
input enable;
input down_count;
output [14:0] counter_out;
output clock_enable;
wire clock;
wire enable;
wire down_count;
reg [14:0] counter_out;
wire clock_enable;
assign clock_enable=(enable)?!clock:1'b0; //clock enable output opposite the counter update
initial
begin
counter_out=15'b000000000000000;
end
//SRAM counter
//controlled by:
// enable (active high)
// down_count (active high = deincrement)
always @ (posedge clock)
begin
if(enable) begin //if enabled
if(down_count) begin //if down count
counter_out <= counter_out - 1; //down count
end
else begin //if up count
counter_out <= counter_out + 1; //up count
end
end
end // End of Block COUNTER
endmodule // End of Module counter
Needed to add an initial block or it doesn't know what the first add value is. This code works, as shown below. Now to add a settable down-counter that can stop the SRAM counter.
Only a register can be manipulated in an always block, as I understand it, so I think it needs to be that way.
http://www.asic-world.com/verilog/verilog_one_day3.html (http://www.asic-world.com/verilog/verilog_one_day3.html)
I dont think we can say
assign counter_out = counter_out - 1;
(then it could be wire) because it it more than combinatorial logic. (these concepts are all a little fuzzy still, so forgive me if I'm spreading mis information or using some of the terms incorrectly).
Here's an example of how Xilinx converts their schematic counter into VHDL and Verilog:
http://www.xilinx.com/itp/xilinx5/data/ ... 86_70.html (http://www.xilinx.com/itp/xilinx5/data/docs/lib/lib0086_70.html)
/*
isim force add {/sram_counter/clock} 1 -radix bin -value 0 -radix bin -time 500 ps -repeat 1 ns
isim force add {/sram_counter/enable} 1 -radix bin
isim force add {/sram_counter/down_count} 0 -radix bin
isim force add {/sram_counter/trigger} 0 -radix bin
*/
module sram_counter (
clock,
enable,
counter_out,
clock_enable,
down_count,
trigger
//counter_set
);
input clock;
input enable;
input down_count;
//input counter_set;
//input [14:0] counter_in;
input trigger;
output [14:0] counter_out;
output clock_enable;
wire clock;
wire enable;
wire down_count;
//wire counter_set;
wire trigger;
reg [14:0] counter_down; //sample down counter
reg stop_count;
reg [14:0] counter_out;//sram address counter
wire clock_enable;
assign clock_enable=(enable&& !stop_count)?!clock:1'b0; //clock enable output opposite the counter update
initial
//assign enable=1'b1;
//assign down_count=1'b0;
begin
counter_out=15'b000000000000000;
counter_down=15'b000000000011111;
stop_count=1'b0;
end
//SRAM counter
//controlled by:
// enable (active high)
// down_count (active high = deincrement)
always @ (posedge clock)
begin
if(enable && !stop_count) begin //if enabled
if(down_count) begin//if down count
counter_out <= counter_out - 1; //down count
end
else begin//if up count
counter_out <= counter_out + 1; //up count
if(trigger && !stop_count) begin
if(counter_down==15'b000000000000000) begin
stop_count<=1'b1;
end
else begin
counter_down<=counter_down-1;
end
end
end
end
end // End of Block COUNTER
endmodule // End of Module counter
Really messy, just playing in the simulator, lots of obvious better things to be done. This is really just a place holder because I need to do a little debugging work on another project.
This code has the SRAM address counter, but also a second down counter. When the down counter reaches 0 it inhibits further counting and disables the clock output.
Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
32/72 (45%) 135/360 (38%) 31/72 (44%) 20/34 (59%) 85/216 (40%)
The key is that it only uses twice the macrocells as the first one, that was exactly what I had hoped.
My next major concern is being able to load the counter from the PIC. How many resources will that take. I think this part will make or break the project. The clock divider and trigger will hopefully be very manageable in size, but they also need to be loaded. It might take one extra macroell for each bit we want to shift in, so 15 for the counter alone. Maybe not. Maybe a limited set of ratios chosen with a BCD input would be a better choice.
Stay tuned to find out ;)
/*
isim force add {/sram_counter/clock} 1 -radix bin -value 0 -radix bin -time 500 ps -repeat 1 ns
isim force add {/sram_counter/enable} 1 -radix bin
isim force add {/sram_counter/down_count} 0 -radix bin
isim force add {/sram_counter/trigger} 0 -radix bin
isim force add {/sram_counter/shift_in} 1 -radix bin
isim force add {/sram_counter/shift_load} 0 -radix bin
*/
module sram_counter (
clock,
enable,
counter_out,
clock_enable,
down_count,
trigger,
shift_in,
shift_load
//counter_set
);
input clock;
input enable;
input down_count;
//input counter_set;
//input [14:0] counter_in;
input trigger;
input shift_in;
input shift_load;
output [14:0] counter_out;
output clock_enable;
wire clock;
wire enable;
wire down_count;
//wire counter_set;
wire trigger;
wire shift_in;
wire shift_load;
reg [14:0] counter_down; //sample down counter
reg stop_count;
reg [14:0] counter_out;//sram address counter
wire clock_enable;
assign clock_enable=(enable&& !stop_count)?!clock:1'b0; //clock enable output opposite the counter update
initial
//assign enable=1'b1;
//assign down_count=1'b0;
begin
counter_out=15'b000000000000000;
counter_down=15'b000000000011111;
stop_count=1'b0;
end
//SRAM counter
//controlled by:
// enable (active high)
// down_count (active high = deincrement)
always @ (posedge clock)
begin
if(shift_load) begin//if disable low, enter data with shift register
counter_down = {counter_down[13:0], shift_in};
end
else if(enable && !stop_count) begin //if enabled
if(down_count) begin//if down count
counter_out <= counter_out - 1; //down count
end
else begin//if up count
counter_out <= counter_out + 1; //up count
if(trigger && !stop_count) begin
if(counter_down==15'b000000000000000) begin
stop_count<=1'b1;
end
else begin
counter_down=counter_down-1;
end
end
end
end
end // End of Block COUNTER
endmodule // End of Module counter
This version makes the down counter into a 15bit shift register. When shift_load is high, the clock ticks load the data into the counter from the shift_in pin. Super messy, the point was just to get the size required:
Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
32/72 (45%) 165/360 (46%) 31/72 (44%) 22/34 (65%) 91/216 (43%)
No major additions. Perfect!
Estimated remaining:
2*8=16 for trigger (8bits on/off, 8bits high/low) shift register will be grafted directly on for setting the triggers
4 for clock divider (also needs to be set)
1 for clock manager (PIC or OSC)
------
21 total
A grand total of 53, with resources to spare for various connections, bigger SRAMs, etc.
I think we might even be able to get a parallel->serial out converter from the trigger for free. Then the PIC wouldn't need 8 pins routed to the SRAM to read back data.
Next up: taking on the trigger to figure out how big it will be.
/*
isim force add {/sram_counter/clock} 1 -radix bin -value 0 -radix bin -time 500 ps -repeat 1 ns
isim force add {/sram_counter/enable} 1 -radix bin
isim force add {/sram_counter/down_count} 0 -radix bin
isim force add {/sram_counter/trigger} 0 -radix bin
isim force add {/sram_counter/shift_in} 1 -radix bin
isim force add {/sram_counter/shift_load} 0 -radix bin
*/
module sram_counter (
clock,
enable,
counter_out,
clock_enable,
down_count,
//trigger,
shift_in,
shift_load,
pins
//counter_set
);
input clock;
input enable;
input down_count;
//input counter_set;
//input [14:0] counter_in;
//input trigger;
input shift_in;
input shift_load;
input [7:0] pins;
output [14:0] counter_out;
output clock_enable;
wire clock;
wire enable;
wire down_count;
//wire counter_set;
wire trigger;
wire shift_in;
wire shift_load;
reg [14:0] counter_down; //sample down counter
reg stop_count;
reg [14:0] counter_out;//sram address counter
wire clock_enable;
reg [15:0] trigger_set;
wire [7:0]pins;
assign clock_enable=(enable&& !stop_count)?!clock:1'b0; //clock enable output opposite the counter update
assign trigger=((trigger_set[0] && (pins[0]&trigger_set[8]))
||(trigger_set[1] && (pins[1]&trigger_set[9]))
||(trigger_set[2] && (pins[2]&trigger_set[10]))
||(trigger_set[3] && (pins[3]&trigger_set[11]))
||(trigger_set[4] && (pins[4]&trigger_set[12]))
||(trigger_set[5] && (pins[5]&trigger_set[13]))
||(trigger_set[6] && (pins[6]&trigger_set[14]))
||(trigger_set[7] && (pins[7]&trigger_set[15]))
)?1'b1:1'b0;
initial
//assign enable=1'b1;
//assign down_count=1'b0;
begin
counter_out=15'b000000000000000;
counter_down=15'b000000000011111;
stop_count=1'b0;
//pins=8'b00000000;
trigger_set=16'b1000000000000000;
end
//SRAM counter
//controlled by:
// enable (active high)
// down_count (active high = deincrement)
always @ (posedge clock)
begin
if(shift_load) begin//if disable low, enter data with shift register
trigger_set={trigger_set[14:0], counter_down[14]};
counter_down = {counter_down[13:0], shift_in};
end
else if(enable && !stop_count) begin //if enabled
if(down_count) begin//if down count
counter_out <= counter_out - 1; //down count
end
else begin//if up count
counter_out <= counter_out + 1; //up count
if(trigger && !stop_count) begin
if(counter_down==15'b000000000000000) begin
stop_count<=1'b1;
end
else begin
counter_down=counter_down-1;
end
end
end
end
end // End of Block COUNTER
endmodule // End of Module counter
Added the 8 data input pins and a trigger that looks for direction match.
Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
51/72 (71%) 325/360 (91%) 47/72 (66%) 29/34 (86%) 207/216 (96%)
Running low on function block inputs, used a few more macrocells than planned. There's room to optimize this code though, this is just to get to know it.
/*
isim force add {/sram_counter/clock} 1 -radix bin -value 0 -radix bin -time 500 ps -repeat 1 ns
isim force add {/sram_counter/enable} 1 -radix bin
isim force add {/sram_counter/down_count} 0 -radix bin
isim force add {/sram_counter/trigger} 0 -radix bin
isim force add {/sram_counter/shift_in} 1 -radix bin
isim force add {/sram_counter/shift_load} 0 -radix bin
isim force add {/sram_counter/pins} 01111111 -radix bin
*/
module sram_counter (
clock,
enable,
counter_out,
clock_enable,
down_count,
//trigger,
shift_in,
shift_load,
pins
//counter_set
);
input clock;
input enable;
input down_count;
//input counter_set;
//input [14:0] counter_in;
//input trigger;
input shift_in;
input shift_load;
input [7:0] pins;
output [14:0] counter_out;
output clock_enable;
wire clock;
wire enable;
wire down_count;
//wire counter_set;
wire trigger;
wire shift_in;
wire shift_load;
reg [14:0] counter_down; //sample down counter
reg stop_count;
reg [14:0] counter_out;//sram address counter
wire clock_enable;
reg [15:0] trigger_set;
wire [7:0]pins;
assign clock_enable=(enable&& !stop_count)?!clock:1'b0; //clock enable output opposite the counter update
assign trigger=( (trigger_set[0] && (pins[0]&&(!trigger_set[8]))) )?1'b1:1'b0;
/* ||(trigger_set[1] && (!(pins[1]^trigger_set[9])))
||(trigger_set[2] && (!(pins[2]^trigger_set[10])))
||(trigger_set[3] && (!(pins[3]^trigger_set[11])))
||(trigger_set[4] && (!(pins[4]^trigger_set[12])))
||(trigger_set[5] && (!(pins[5]^trigger_set[13])))
||(trigger_set[6] && (!(pins[6]^trigger_set[14])))
||(trigger_set[7] && (!(pins[7]^trigger_set[15])))
)?1'b1:1'b0;*/
initial
//assign enable=1'b1;
//assign down_count=1'b0;
begin
counter_out=15'b000000000000000;
counter_down=15'b000000000011111;
stop_count=1'b0;
//pins=8'b00000000;
trigger_set=16'b1000000000000000;
end
//SRAM counter
//controlled by:
// enable (active high)
// down_count (active high = deincrement)
always @ (posedge clock)
begin
if(shift_load) begin//if disable low, enter data with shift register
trigger_set={trigger_set[14:0], counter_down[14]};
counter_down = {counter_down[13:0], shift_in};
end
else if(enable && !stop_count) begin //if enabled
if(down_count) begin//if down count
counter_out <= counter_out - 1; //down count
end
else begin//if up count
counter_out <= counter_out + 1; //up count
if(trigger && !stop_count) begin
if(counter_down==15'b000000000000000) begin
stop_count<=1'b1;
end
else begin
counter_down=counter_down-1;
end
end
end
end
end // End of Block COUNTER
endmodule // End of Module counter
The previous and current version (above) do not work in simulation. The update (that should work) uses 50% fewer function block inputs, but still no trigger on an edge match. Will probably continue tomorrow, enough for today, I'll be dreaming about it :)
/*
isim force add {/sram_counter/clock} 1 -radix bin -value 0 -radix bin -time 500 ps -repeat 1 ns
isim force add {/sram_counter/enable} 1 -radix bin
isim force add {/sram_counter/down_count} 0 -radix bin
isim force add {/sram_counter/shift_in} 1 -radix bin
isim force add {/sram_counter/shift_load} 0 -radix bin
isim force add {/sram_counter/pins} 01111111 -radix bin
isim force add {/sram_counter/trigger} 0 -radix bin
*/
module sram_counter (
clock,
enable,
counter_out,
clock_enable,
down_count,
//trigger,
shift_in,
shift_load,
pins
//pin
//counter_set
);
input clock;
input enable;
input down_count;
//input counter_set;
//input [14:0] counter_in;
//input trigger;
input shift_in;
input shift_load;
input [7:0] pins;
// input pin;
output [14:0] counter_out;
output clock_enable;
wire clock;
wire enable;
wire down_count;
//wire counter_set;
wire trigger;
wire shift_in;
wire shift_load;
reg [14:0] counter_down; //sample down counter
reg stop_count;
reg [14:0] counter_out;//sram address counter
wire clock_enable;
reg [15:0] trigger_set;
wire [7:0]pins;
//reg pin;
//reg trig_edge;
//reg trig_enable;
assign clock_enable=(enable&& !stop_count)?!clock:1'b0; //clock enable output opposite the counter update
//assign trigger=(trig_enable &&(pin&&(!trig_edge)))?1'b0:1'b1;
// assign trigger=(1'b1 &&(1'b1&&(!1'b1)))?1'b1:1'b0;
assign trigger=( (trigger_set[0] && (pins[0]&&(!trigger_set[8])))
||(trigger_set[1] && (!(pins[1]^trigger_set[9])))
||(trigger_set[2] && (!(pins[2]^trigger_set[10])))
||(trigger_set[3] && (!(pins[3]^trigger_set[11])))
||(trigger_set[4] && (!(pins[4]^trigger_set[12])))
||(trigger_set[5] && (!(pins[5]^trigger_set[13])))
||(trigger_set[6] && (!(pins[6]^trigger_set[14])))
||(trigger_set[7] && (!(pins[7]^trigger_set[15])))
)?1'b1:1'b0;
initial
//assign enable=1'b1;
//assign down_count=1'b0;
begin
counter_out=15'b000000000000000;
counter_down=15'b000000000011111;
stop_count=1'b0;
//pins=8'b00000000;
trigger_set=16'b1000000000000000;
// pin=1'b1;
// trig_edge=1'b1;
// trig_enable=1'b0;
end
//SRAM counter
//controlled by:
// enable (active high)
// down_count (active high = deincrement)
always @ (posedge clock)
begin
if(shift_load) begin//if disable low, enter data with shift register
trigger_set={trigger_set[14:0], counter_down[14]};
counter_down = {counter_down[13:0], shift_in};
end
else if(enable && !stop_count) begin //if enabled
if(down_count) begin//if down count
counter_out <= counter_out - 1; //down count
end
else begin//if up count
counter_out <= counter_out + 1; //up count
if(trigger && !stop_count) begin
if(counter_down==15'b000000000000000) begin
stop_count<=1'b1;
end
else begin
counter_down=counter_down-1;
end
end
end
end
end // End of Block COUNTER
endmodule // End of Module counter
This works, but trigger is inverted. Will fix it and optimize tomorrow. What a hoot! Fun project.
Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
49/72 (69%) 241/360 (67%) 47/72 (66%) 29/34 (86%) 160/216 (75%)
Off-topic, but found this just browsing: http://www.ikalogic.com/scanalogic2/index.php (http://www.ikalogic.com/scanalogic2/index.php)
Lower speed than cpld/fpga based ones, intentionally low bit count, but interesting idea imo.
I think the idea could be turned into any nbits * 20MSPS * 64 or 256 kSamples LA, without having to deal with address generation. Advanced triggering would probably be impossible with the avr... Thoughts?
http://dangerousprototypes.com/docs/Log ... c_analyzer (http://dangerousprototypes.com/docs/Logic_Shrimp_logic_analyzer)
:)
But it has its own difficulties. You don't have to generate the address, but you do have to track the SRAM address in the uC because there's no way to know where it stops at the end of capture. To read data back you have to start at the beginning and go forwards (instead of backwards/down counter) so you have to enter an address to start at.
Triggers are actually pretty manageable because you can tweak the timing to account for the interrupt latency. As long as the buffer isn't set for 100% pre-roll.
assign trigger=( (trigger_set[0] || (pins[0]~^trigger_set[8]))
&&(trigger_set[1] || (pins[1]~^trigger_set[9]))
&&(trigger_set[2] || (pins[2]~^trigger_set[10]))
&&(trigger_set[3] || (pins[3]~^trigger_set[11]))
&&(trigger_set[4] || (pins[4]~^trigger_set[12]))
&&(trigger_set[5] || (pins[5]~^trigger_set[13]))
&&(trigger_set[6] || (pins[6]~^trigger_set[14]))
&&(trigger_set[7] || (pins[7]~^trigger_set[15]))
)?1'b1:1'b0;
This is the ideal trigger. Unlike the previous trigger, this requires that all pins match the set trigger, instead of just one.
trigger_set[7:0] set the trigger on (0) or off (1)
trigger_set[15:8] set the trigger state high (1) or low (0)
What happens:
trigger_set[0] if active is 0, then pins[0] and trigger_set[8] must be the same (xnor ~^) to equal 1
if trigger_set[0] is not active (1), the result of pins[0]~^trigger_set[8] doesn't matter because the expression always ORs to 1
&& means all the statements must be true (1) for trigger to be 1
Unfortunately, it does not fit. It takes 490 product terms to fit all the interconnections between the triggers, there are only 360 total in the device. When we change && to || (any one match) it fits nicely.
Will look at different triggers, but worse comes to worse a single pin trigger is ok for messing around.
assign trigger=( (trigger_set[7:0] || (pins[7:0]~^trigger_set[15:8])));
This appears to fit, but doesn't work.
assign trigger=( (trigger_set[7:0] || (!(pins[7:0]^trigger_set[15:8]))))?1'b1:1'b0;
This probably works, but doesn't fit.
That's all the play time I have for now :(
I didn't want to do this because I'm trying to figure it out on my own, but I looked into the trigger.v from the Demo Core to see how it worked.
//
// IED - Shift register initialization handler...
//
// Much more space efficient in FPGA to compare this way.
//
// Instead of four seperate 32-bit value, 32-bit mask, and 32-bit comparison
// functions & all manner of flops & interconnect, each stage uses LUT table
// lookups instead.
//
// Each LUT RAM evaluates 4-bits of input. The RAM is programmed to
// evaluate the original masked compare function, and is serially downloaded
// by the following verilog.
//
//
// Background:
// ----------
// The original function was:
// hit = ((dataIn[31:0] ^ value[31:0]) & mask[31:0])==0;
//
//
// The following table shows the result for a single bit:
// dataIn value mask hit
// x x 0 1
// 0 0 1 1
// 0 1 1 0
// 1 0 1 0
// 1 1 1 1
//
// If a mask bit is zero, it always matches. If one, then
// the result of comparing dataIn & value matters. If dataIn &
// value match, the XOR function results in zero. So if either
// the mask is zero, or the input matches value, you get a hit.
//
//
// New code
// --------
// To evaluate the dataIn, each address of the LUT RAM's evalutes:
// What hit value should result assuming my address as input?
//
// In other words, LUT for dataIn[3:0] stores the following at given addresses:
// LUT address 0 stores result of: (4'h0 ^ value[3:0]) & mask[3:0])==0
// LUT address 1 stores result of: (4'h1 ^ value[3:0]) & mask[3:0])==0
// LUT address 2 stores result of: (4'h2 ^ value[3:0]) & mask[3:0])==0
// LUT address 3 stores result of: (4'h3 ^ value[3:0]) & mask[3:0])==0
// LUT address 4 stores result of: (4'h4 ^ value[3:0]) & mask[3:0])==0
// etc...
//
// The LUT for dataIn[7:4] stores the following:
// LUT address 0 stores result of: (4'h0 ^ value[7:4]) & mask[7:4])==0
// LUT address 1 stores result of: (4'h1 ^ value[7:4]) & mask[7:4])==0
// LUT address 2 stores result of: (4'h2 ^ value[7:4]) & mask[7:4])==0
// LUT address 3 stores result of: (4'h3 ^ value[7:4]) & mask[7:4])==0
// LUT address 4 stores result of: (4'h4 ^ value[7:4]) & mask[7:4])==0
// etc...
//
// Eight LUT's are needed to evalute all 32-bits of dataIn, so the
// following verilog computes the LUT RAM data for all simultaneously.
//
//
// Result:
// ------
// It functionally does exactly the same thing as before. Just uses
// less FPGA. Only requirement is the Client software on your PC issue
// the value & mask's for each trigger stage in pairs.
//
reg [31:0] maskRegister, next_maskRegister;
reg [31:0] valueRegister, next_valueRegister;
reg [3:0] wrcount, next_wrcount;
reg [3:0] wrenb, next_wrenb;
reg [7:0] wrdata;
initial
begin
wrcount=0;
wrenb=4'b0;
end
always @ (posedge clock)
begin
maskRegister = next_maskRegister;
valueRegister = next_valueRegister;
wrcount = next_wrcount;
wrenb = next_wrenb;
end
always @*
begin
next_wrcount = 0;
// Capture data during mask write...
next_maskRegister = (|wrMask) ? config_data : maskRegister;
next_valueRegister = (|wrValue) ? config_data : valueRegister;
// Do 16 writes when value register written...
next_wrenb = wrenb | wrValue;
if (|wrenb)
begin
next_wrcount = wrcount+1'b1;
if (&wrcount) next_wrenb = 4'h0;
end
// Compute data for the 8 target LUT's...
wrdata = {
~|((~wrcount^valueRegister[31:28])&maskRegister[31:28]),
~|((~wrcount^valueRegister[27:24])&maskRegister[27:24]),
~|((~wrcount^valueRegister[23:20])&maskRegister[23:20]),
~|((~wrcount^valueRegister[19:16])&maskRegister[19:16]),
~|((~wrcount^valueRegister[15:12])&maskRegister[15:12]),
~|((~wrcount^valueRegister[11:8])&maskRegister[11:8]),
~|((~wrcount^valueRegister[7:4])&maskRegister[7:4]),
~|((~wrcount^valueRegister[3:0])&maskRegister[3:0])};
if (reset)
begin
next_wrcount = 0;
next_wrenb = 4'h0;
end
end
I tried the old version:
assign trigger= ((pins[7:0] ^ trigger_set[15:8]) & trigger_set[7:0]);
But it doesn't simulate correctly, I'll look into why now.
always @*
begin
// Compute data for the 8 target LUT's...
wrdata = {
~|((~wrcount^valueRegister[31:28])&maskRegister[31:28]),
~|((~wrcount^valueRegister[27:24])&maskRegister[27:24]),
~|((~wrcount^valueRegister[23:20])&maskRegister[23:20]),
~|((~wrcount^valueRegister[19:16])&maskRegister[19:16]),
~|((~wrcount^valueRegister[15:12])&maskRegister[15:12]),
~|((~wrcount^valueRegister[11:8])&maskRegister[11:8]),
~|((~wrcount^valueRegister[7:4])&maskRegister[7:4]),
~|((~wrcount^valueRegister[3:0])&maskRegister[3:0])};
end
I may also look at dogsbody's updated version, see if I can fit it better in the CPLD by breaking it own and tweaking placement.
assign trigger=((pins[7:0] ^ trigger_value[7:0]) & trigger_mask[7:0])==0;
//// The original function was:
// hit = ((dataIn[31:0] ^ value[31:0]) & mask[31:0])==0;
This does work, but it is also huge (470pterms).
Maybe it can be broken up like IED did in the core to better suit the structure of the CPLD. I guess it it string to flatten it out for max speed and big equations cause issues. Did I say the trigger was going to be easy up the thread somewhere ;) I've done this before via schematic in the same CPLD, but probably with no other goodies.
/*
isim force add {/sram_counter/clock} 1 -radix bin -value 0 -radix bin -time 500 ps -repeat 1 ns
isim force add {/sram_counter/down_count} 0 -radix bin
isim force add {/sram_counter/shift_in} 1 -radix bin
isim force add {/sram_counter/setup} 0 -radix bin
isim force add {/sram_counter/arm} 0 -radix bin
isim force add {/sram_counter/pins} 00000000 -radix bin
isim force add {/sram_counter/trigger_value} 00000001 -radix bin
isim force add {/sram_counter/trigger_mask} 00000001 -radix bin
isim force add {/sram_counter/sample_reg} 000000000000000 -radix bin
isim force add {/sram_counter/trigger} 0 -radix bin
*/
module sram_counter (
pic_clock,
osc_clock,
sram_address,
shift_in,
setup,
arm,
done,
down_count,
pins,
);
//global
input wire pic_clock;
input wire osc_clock;
input wire shift_in;
input wire setup;
input wire [7:0] pins;
input wire down_count;
input wire arm;
output wire done;
output reg [14:0] sram_address;
//local
// (* keep = "true" *) reg [7:0] trigger_mask;
// (* keep = "true" *) reg [7:0] trigger_value;
reg [7:0] trigger_mask;
reg [7:0] trigger_value;
reg [15:0] sample_counter;
reg [3:0] sample_reg;
reg [3:0] clkdiv;
assign done=(sample_counter[15]==0);
wire clock;
assign clock=(setup)?pic_clock:clkdiv[3];
wire trigger;
assign trigger = ((pins[7:0] ^ trigger_value[7:0]) & trigger_mask[7:0])==0;
initial
begin
sram_address=15'b000000000000000;
sample_counter=16'b1000000000001111;
end
//2x15
// Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
//65/72 (91%) 189/360 (53%) 63/72 (88%) 29/34 (86%) 132/216 (62%)
//Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
//64/72 (89%) 187/360 (52%) 62/72 (87%) 29/34 (86%) 126/216 (59%)
//Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
//64/72 (89%) 187/360 (52%) 62/72 (87%) 29/34 (86%) 126/216 (59%)
//Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
//53/72 (74%) 154/360 (43%) 51/72 (71%) 29/34 (86%) 136/216 (63%)
//with clock switch
//Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
//54/72 (75%) 207/360 (58%) 51/72 (71%) 30/34 (89%) 116/216 (54%)
//with simple clock divider
//Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
//58/72 (81%) 210/360 (59%) 55/72 (77%) 30/34 (89%) 121/216 (57%)
always @ (posedge osc_clock)
begin
clkdiv<=clkdiv+1;
end
always @ (posedge clock)
begin
if(!done) begin
if(down_count) begin//if down count
sram_address <= sram_address - 1; //down count
end
else begin//if up count
sram_address <= sram_address + 1; //up count
end
end
end
always @ (posedge clock)
begin
if(setup) begin
trigger_value={trigger_value[6:0], trigger_mask[7]};
trigger_mask={trigger_mask[6:0], sample_reg[3]};
sample_reg = {sample_reg[3:0], shift_in}; //load samples into REG first.
end
else if(arm) begin
sample_counter[15]=1'b1;
sample_counter[14:12]=sample_reg[3:1]; //must use this half-step or there are pterm issues
sample_counter[11:1]=11'b00000000000;
sample_counter[0]=sample_reg[0];
end
else if (trigger && (!done)) begin
sample_counter=sample_counter-1;
end
end
endmodule // End of Module counter
Code cleaned and revamped.
Got some help with the triggers on the Xilinx forum. The issue wasn't the trigger, but the way I loaded the trigger and counter together from the shift register. This adds a second register which is put into the counter when an ARM pin is high.
In this code I started to make the sample counter load register fewer bits so we reduce the macrocells used. This part isn't done yet.
Also added:
*clock switch from PIC to OSC
*4 bit clock divider (not yet configurable)
Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
58/72 (81%) 210/360 (59%) 55/72 (77%) 30/34 (89%) 121/216 (57%)
This is almost it. I will probably finish in a day or two. It will be quite limited, but I think we can get a nice and usable 100MHz LA from a 72macrocell CPLD :)
/*
isim force add {/sram_counter/osc_clock} 1 -radix bin -value 0 -radix bin -time 500 ps -repeat 1 ns
isim force add {/sram_counter/down_count} 0 -radix bin
isim force add {/sram_counter/shift_in} 1 -radix bin
isim force add {/sram_counter/setup} 0 -radix bin
isim force add {/sram_counter/arm} 0 -radix bin
isim force add {/sram_counter/pins} 00000000 -radix bin
isim force add {/sram_counter/trigger_value} 00000001 -radix bin
isim force add {/sram_counter/trigger_mask} 00000001 -radix bin
isim force add {/sram_counter/sample_reg} 000000000000000 -radix bin
isim force add {/sram_counter/speed} 111 -radix bin
isim force add {/sram_counter/trigger} 0 -radix bin
*/
module sram_counter (
pic_clock,
osc_clock,
sram_address,
shift_in,
setup,
arm,
done,
down_count,
pins,
speed
);
//global
input wire pic_clock;
input wire osc_clock;
input wire shift_in;
input wire setup;
input wire [7:0] pins;
input wire down_count;
input wire arm;
input wire [3:0] speed;
output wire done;
output reg [14:0] sram_address;
//local
// (* keep = "true" *) reg [7:0] trigger_mask;
// (* keep = "true" *) reg [7:0] trigger_value;
reg [7:0] trigger_mask;
reg [7:0] trigger_value;
reg [15:0] sample_counter;
reg [14:0] sample_reg;
reg [2:0] clkdiv;
assign done=(sample_counter[15]==0);
reg sample_clock;
wire clock;
assign clock=(setup)?pic_clock:((speed[0])?sample_clock:osc_clock);
wire trigger;
assign trigger = ((pins[7:0] ^ trigger_value[7:0]) & trigger_mask[7:0])==0;
initial
begin
sram_address=15'b000000000000000;
sample_counter=16'b1000000000001111;
clkdiv=3'b000;
sample_clock=1'b0;
end
//2x15
// Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
//65/72 (91%) 189/360 (53%) 63/72 (88%) 29/34 (86%) 132/216 (62%)
//Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
//64/72 (89%) 187/360 (52%) 62/72 (87%) 29/34 (86%) 126/216 (59%)
//Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
//64/72 (89%) 187/360 (52%) 62/72 (87%) 29/34 (86%) 126/216 (59%)
//Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
//53/72 (74%) 154/360 (43%) 51/72 (71%) 29/34 (86%) 136/216 (63%)
//with clock switch
//Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
//54/72 (75%) 207/360 (58%) 51/72 (71%) 30/34 (89%) 116/216 (54%)
//with simple clock divider
//Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
//58/72 (81%) 210/360 (59%) 55/72 (77%) 30/34 (89%) 121/216 (57%)
always @ (posedge osc_clock)
begin
if(clkdiv==3'b000) begin
clkdiv<=speed;
sample_clock<=sample_clock-1;
end
else begin
clkdiv<=clkdiv-1;
end
end
always @ (posedge clock)
begin
if(!done) begin
if(down_count) begin//if down count
sram_address <= sram_address - 1; //down count
end
else begin//if up count
sram_address <= sram_address + 1; //up count
end
end
end
always @ (posedge clock)
begin
if(setup) begin
trigger_value={trigger_value[6:0], trigger_mask[7]};
trigger_mask={trigger_mask[6:0], sample_reg[14]};
sample_reg = {sample_reg[13:0], shift_in}; //load samples into REG first.
end
else if(arm) begin
sample_counter[15]<=1'b1;
sample_counter[14:0]<=sample_reg[14:0]; //must use this half-step or there are pterm issues
end
else if (trigger && (!done)) begin
sample_counter<=sample_counter-1;
end
end
endmodule // End of Module counter
Current version. With clock divider (3 bits, or none). Getting really close. Close to full use too....
Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
68/72 (95%) 241/360 (67%) 66/72 (92%) 33/34 (98%) 136/216 (63%)
I really think I can do it though. Only a few things left, and then to assign pins. We can always strip out some stuff, but the strategy is to do the best small design possible and then strip down/optimize.
/*
Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
68/72 (95%) 229/360 (64%) 65/72 (91%) 34/34 (100%) 147/216 (69%)
*/
/*
isim force add {/sram_counter/osc_clock} 1 -radix bin -value 0 -radix bin -time 500 ps -repeat 1 ns
isim force add {/sram_counter/pic_clock} 1 -radix bin -value 0 -radix bin -time 500 ps -repeat 2 ns
isim force add {/sram_counter/ext_clock} 1 -radix bin -value 0 -radix bin -time 500 ps -repeat 3 ns
isim force add {/sram_counter/mode_setup} 0 -radix bin
isim force add {/sram_counter/mode_run} 0 -radix bin
isim force add {/sram_counter/mode_read} 0 -radix bin
isim force add {/sram_counter/pins} 00000000 -radix bin
isim force add {/sram_counter/trigger_value} 00000001 -radix bin
isim force add {/sram_counter/trigger_mask} 00000001 -radix bin
isim force add {/sram_counter/sample_reg} 0000000000111 -radix bin
isim force add {/sram_counter/mode_clock} 0001 -radix bin
isim force add {/sram_counter/setup_reg} 00000001 -radix bin
isim force add {/sram_counter/trigger} 0 -radix bin
*/
module sram_counter (
pic_clock,
osc_clock,
sram_clock,
sram_address,
pins,
mode_setup,
mode_run,
mode_read,
mode_clock,
done
);
//global
input wire pic_clock;
input wire osc_clock;
input wire [7:0] pins;
input wire mode_setup;
input wire mode_run;
input wire mode_read;
input wire [3:0] mode_clock;
output wire done;
output reg [14:0] sram_address;
output wire sram_clock;
//local
// (* keep = "true" *) reg [7:0] trigger_mask;
reg trigger_match;
wire trigger;
reg [15:0] sample_counter;//holds trigger and sample counter settings
//reg [7:0] trigger_value;
//reg [15:8] trigger_mask;
//reg [28:16] sample_reg; //holds sample count,
//SUMP protocol gives samples in /4,
//so we can use two bits less thans sample_counter
reg [28:0] setup_reg; //all registers in one big one works best
//registers for the clock divider
reg [2:0] clock_divider; //subtract from here to divide the clock
reg div_clock; //divider output signal
assign master_clock=(mode_clock[0])?div_clock:((mode_clock[1])?osc_clock:((mode_clock[2])?pins[0]:pic_clock));
//assign trigger=(((pins[7:0] ^ trigger_value[7:0]) & trigger_mask[7:0])==0);
assign trigger=(((pins[7:0] ^ setup_reg[7:0]) & setup_reg[15:8])==0);
assign done=(sample_counter[15]==0); //we are done when the top bit of sample counter flips to 0
assign sram_clock=(!done||mode_read)?master_clock:1'b0;
initial
begin
sram_address=15'b000000000000000;
sample_counter=16'b1000000000001111;
//sample_reg=13'b0000000000111;
clock_divider=3'b000;
div_clock=1'b0;
trigger_match=1'b0;
end
always @ (posedge osc_clock)
begin
if(clock_divider==3'b000) begin
clock_divider<=mode_clock[3:1];//reuse clock setup 3:1 as divider value
div_clock<=div_clock-1;
end
else begin
clock_divider<=clock_divider-1;
end
end
always @ (posedge master_clock)
begin
if(mode_setup) begin
setup_reg={setup_reg[27:0], mode_read}; //in setup mode reuse bit 3 as SDI
end
else if(mode_run) begin
sample_counter[15]<=1'b1;
sample_counter[14:2]<=setup_reg[28:16]; //must use this half-step or there are pterm issues
sample_counter[1:0]<=2'b00;
trigger_match<=1'b0;
end
else if (mode_read) begin
sram_address <= sram_address - 1; //down count
end
else if(!done) begin
sram_address <= sram_address + 1; //up count
if (trigger_match || trigger) begin
trigger_match<=1'b1;
sample_counter<=sample_counter-1;
end
end
end
endmodule // End of Module counter
What a good time :) I consider this a v0.1. It simulates great, and we'll start to design hardware for it to see if the pinout will work.
Macrocells Used Pterms Used Registers Used Pins Used Function Block Inputs Used
68/72 (95%) 230/360 (64%) 65/72 (91%) 34/34 (100%) 147/216 (69%)
I managed to fit a whole lot of stuff in it:
*44 pin CPLD drives a 32Kx8 sram
*8bit trigger with pin direction (could use a little work)
*/2-/7 clock divider
*select from multiple clock sources (OSC, OSC with divider, PIC, or external clock from pin 0).
*configurable 0-100% pre/post roll samples
Each register bit takes a macrocell, so a lot of configuration is done with pins instead of serially. We exchanged a very pin-intense design for a bigger CPLD.
There are 3 mode select pin (setup, arm, read) (arm reused as serial data for setup mode)
There are 4 clock pins that toggle the clock source. 3:0 are reused to set the divider value in osc_divider mode
Unfortunately the PIC that drives it will need a lot of pins, and will also be stuck doing stuff like changing the SRAM read/write mode pin. It might be a good idea to move to a 64pin CPLD for a few more pins for that extra stuff (R/W, input buffer enable, ?)
A datasheet and some diagrams are on the way.
Here's a diagram of the current CPLD implementation.