Recently I pulled out my boxes of 7400 series logic (working on a few old school projects that were idled for a bit). There's something special about these chips...

So, on a whim I thought that I'd bang out verilog modules for the chips I have on hand. I'm putting the files for these modules in the tanukifu project

here.
There are a lot of times one wants to step through a sequence of events, cause a certain number of events to occur or wait until a certain number of events have occurred before doing something... for these things counters are nice to have available. If you chain flip-flops/registers together so that the output from one triggers the input clock on the next in line we can count easily in base two.

The 74x90 is a BCD counter - oftentimes we need to convert to base 10 (because people commonly use base 10) and one way to do that is to combine a counter that goes to 5 and a second counter that goes to 2 - depending on how it's wired up we can have a 1:2 counter/divider and a 1:5 counter/divider or a 1:10 counter/divider.

The hdl in verilog for this can be done in a number of ways, but I'm doing it here in a way where it's easy to see how one state changes to the next (but this isn't an efficient way)...

// the div2 section is tied to the Q0 output Aset goes high if the async set/reset triggers occur

// but otherwise toggle based on the clk0.

always @ (negedge clk0 or posedge Aset) begin

if (Aset) begin

// if preset or clear states indicate we should set intent

Qint0 <= Q0;

end

else if (!Aset) begin

// toggle state

Qint0 <= ~Qint0;

end

end

// the div5 section is tied to clk1

always @ (negedge clk1 or posedge Aset) begin

if (Aset) begin

// if preset or clear states indicate we should set intent

Qint1 <= Q1;

Qint2 <= Q2;

Qint3 <= Q3;

end

else if (!Aset) begin

// increase count to 5 then cycle over

if (!Qint3 & !Qint2 & !Qint1) begin

Qint1 <= 1'b1;

Qint2 <= 1'b0;

Qint3 <= 1'b0;

end

else if (!Qint3 & !Qint2 & Qint1) begin

Qint1 <= 1'b0;

Qint2 <= 1'b1;

Qint3 <= 1'b0;

end

else if (!Qint3 & Qint2 & !Qint1) begin

Qint1 <= 1'b1;

Qint2 <= 1'b1;

Qint3 <= 1'b0;

end

else if (!Qint3 & Qint2 & Qint1) begin

Qint1 <= 1'b0;

Qint2 <= 1'b0;

Qint3 <= 1'b1;

end

else begin

Qint1 <= 1'b0;

Qint2 <= 1'b0;

Qint3 <= 1'b0;

end

end

end

// async. set and clear logic, Aset is used to pass the async signals back to the output register

// state determination.

always @ (*) begin

if (!(reset0 & reset1) & (set0 & set1)) begin

// set output high (1001)

Q0 <= 1'b1;

Q1 <= 1'b0;

Q2 <= 1'b0;

Q3 <= 1'b1;

Aset <= 1'b1;

end

else if (reset0 & reset1) begin

// clear output

Q0 <= 1'b0;

Q1 <= 1'b0;

Q2 <= 1'b0;

Q3 <= 1'b0;

Aset <= 1'b1;

end

else begin

// change state

Q0 <= Qint0;

Q1 <= Qint1;

Q2 <= Qint2;

Q3 <= Qint3;

Aset <= 1'b0;

end

end // always @ (*)

If instead of base 10 one wanted just a 4 bit counter it's easier to describe in verilog because we can imply the connections between the registers.

This is a 74x161... it's actually a bit more complicated than the 74x90 because it allow parallel loading of the registers and includes a terminal count output that makes it easier to chain multiple counters together... The 74x160 and 74x162 are BCD counters and the 74x161 and 74x163 are binary counters. The 74x160 and 74x161 have async. resets and the 74x162 and 74x163 have clock sync. resets.

// assign for terminal count output

assign TC = Q & 4'b1111; // state 1111

always @ (posedge clock or negedge reset) begin

if (!reset) begin

Q <= 4'b0000;

end

else if (reset) begin

if (!PE) begin

// parallel load

Q <= P;

end

else if (PE & CET & CEB) begin

Q <= Q + 1'b1;

end

end

end

The 74x590 is one of my more often used chips in the drawer because it increases the counter size to 8 bits and adds the ability to tristate the outputs. It also has a separate register clock so the internal counter can be changed without making those changes visible on the outputs until a separate clock signal occurs.

// tristate the outputs

assign Q = (G) ? 8'bzzzzzzzz : Qreg;

// RCO is the saturation flag

assign RCO = Qreg & 8'b11111111;

// copy the internal counts to the registers on RCK

always @ (posedge RCK) begin

Qreg <= Qint;

end

// reset applies to the internal counters.

// increment on CCK

always @ (posedge CCK or negedge reset) begin

if (!reset) begin

Qint <= 8'b00000000;

end

else if (reset & CCKEN) begin

Qint <= Qint + 1;

end

end

While there are lots of variants for the counter chips, I will mention here the 74x569 which adds the ability to count both up or down. like the other variants there is a bcd version (74x568) but it's easier to understand the binary counters. It also includes both sync. and async. resets... yay... This is one of my favorites...

// assign for terminal count output

assign TC = Qreg & 4'b1111; // state 1111

// tristates for output enable (OE)

assign Q = (OE) ? 4'bz : Qreg;

// CC clock propagation

assign CC = (SR & PE & !CET & !CEP & !TC) ? clock : 1'b1;

// up/down counter - sync and async reset, 2 enables, direction

always @ (posedge clock or negedge reset) begin

if (!reset) begin

// async. reset

Qreg <= 4'b0000;

end

else if (reset) begin

if (!SR) begin

// sync. reset

Qreg <= 4'b0000;

end

else if (SR & !PE) begin

// parallel load

Qreg <= D;

end

else if (SR & !CEP & !CET & UD) begin

Qreg <= Qreg + 1;

end

else if (SR & !CEP & !CET & !UD) begin

Qreg <= Qreg - 1;

end

end

end

I'd also like to mention the 74x4520... this chip contains two 4 bits counters and while it doesn't have parallel load or up/down directional control or easy chaining signals it has the ability to use either the positive edge of the clock or the negative edge of the enable line to increment the counter.

The hdl is easy in verilog...

assign tripA = !(!clkA & enA);

assign tripB = !(!clkB & enB);

always @ (posedge tripA or posedge resetA) begin

if (resetA) begin

// async. reset

QA <= 4'b0000;

end

else if (!resetA) begin

QA <= QA + 1;

end

end

always @ (posedge tripB or posedge resetB) begin

if (resetB) begin

// async. reset

QB <= 4'b0000;

end

else if (!resetB) begin

QB <= QB + 1;

end

end

Just to give you an idea of how nice it is to have verilog do the connections between the registers when they are grouped (so they behave like a multidigit number), let's look at the wrong way to code a 74x568 (just like the 74x569 above but in bcd instead of binary). (This isn't really the "wrong way" but it's an inefficient way - logically treating the 1:2 and 1:5 counters separately and within the 1:5 treating the registers separately and having explicit states for the up as well as the down movements)... perhaps it would be better to say the "ugly and difficult to maintain and understand" way rather than the "wrong way"...

// assign for terminal count output

assign RCO = Q0 & !Q1 & !Q2 & Q3; // state 1001

// tristates for output enable (OE)

assign Q = (OE) ? 4'bz : {Q3, Q2, Q1, Q0};

// Just for fun I'm doing every state with up/down as an input

// rather than the clean way... I'll use the clean way with the

// binary counter version... I'm doing this to show how increasing the

// abilities of a module can become unmanageable... You should quickly

// realize that setting up a simple state machine with external control logic

// would be more efficient...

always @ (posedge clock or negedge reset) begin

if (!reset) begin

Q0 = 1'b0;

Q1 = 1'b0;

Q2 = 1'b0;

Q3 = 1'b0;

end

else if (reset) begin

if (!SR) begin

Q0 = 1'b0;

Q1 = 1'b0;

Q2 = 1'b0;

Q3 = 1'b0;

end

else if (!load) begin

// parallel load

Q0 = A;

Q1 = B;

Q2 = C;

Q3 = D;

end

else if (load & !ENP & !ENT & !Q0 & !Q1 & !Q2 & !Q3 & UD) begin

// state 0000

Q0 = 1'b1;

Q1 = 1'b0;

Q2 = 1'b0;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & Q0 & !Q1 & !Q2 & !Q3 & UD) begin

// state 1000

Q0 = 1'b0;

Q1 = 1'b1;

Q2 = 1'b0;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & !Q0 & Q1 & !Q2 & !Q3 & UD) begin

// state 0100

Q0 = 1'b1;

Q1 = 1'b1;

Q2 = 1'b0;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & Q0 & Q1 & !Q2 & !Q3 & UD) begin

// state 1100

Q0 = 1'b0;

Q1 = 1'b0;

Q2 = 1'b1;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & !Q0 & !Q1 & Q2 & !Q3 & UD) begin

// state 0010

Q0 = 1'b1;

Q1 = 1'b0;

Q2 = 1'b1;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & Q0 & !Q1 & Q2 & !Q3 & UD) begin

// state 1010

Q0 = 1'b0;

Q1 = 1'b1;

Q2 = 1'b1;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & !Q0 & Q1 & Q2 & !Q3 & UD) begin

// state 0110

Q0 = 1'b1;

Q1 = 1'b1;

Q2 = 1'b1;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & Q0 & Q1 & Q2 & !Q3 & UD) begin

// state 1110

Q0 = 1'b0;

Q1 = 1'b0;

Q2 = 1'b0;

Q3 = 1'b1;

end

else if (load & !ENP & !ENT & !Q0 & !Q1 & !Q2 & Q3 & UD) begin

// state 1000

Q0 = 1'b1;

Q1 = 1'b0;

Q2 = 1'b0;

Q3 = 1'b1;

end

else if (load & !ENP & !ENT & Q0 & !Q1 & !Q2 & Q3 & UD) begin

// state 1001

Q0 = 1'b0;

Q1 = 1'b0;

Q2 = 1'b0;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & !Q0 & !Q1 & !Q2 & !Q3 & !UD) begin

// state 1001

Q0 = 1'b1;

Q1 = 1'b0;

Q2 = 1'b0;

Q3 = 1'b1;

end

else if (load & !ENP & !ENT & Q0 & !Q1 & !Q2 & !Q3 & !UD) begin

// state 0000

Q0 = 1'b0;

Q1 = 1'b0;

Q2 = 1'b0;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & !Q0 & Q1 & !Q2 & !Q3 & !UD) begin

// state 1000

Q0 = 1'b1;

Q1 = 1'b0;

Q2 = 1'b0;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & Q0 & Q1 & !Q2 & !Q3 & !UD) begin

// state 0100

Q0 = 1'b0;

Q1 = 1'b1;

Q2 = 1'b1;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & !Q0 & !Q1 & Q2 & !Q3 & !UD) begin

// state 1100

Q0 = 1'b1;

Q1 = 1'b1;

Q2 = 1'b0;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & Q0 & !Q1 & Q2 & !Q3 & !UD) begin

// state 0010

Q0 = 1'b0;

Q1 = 1'b0;

Q2 = 1'b1;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & !Q0 & Q1 & Q2 & !Q3 & !UD) begin

// state 1010

Q0 = 1'b1;

Q1 = 1'b0;

Q2 = 1'b1;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & Q0 & Q1 & Q2 & !Q3 & !UD) begin

// state 0110

Q0 = 1'b0;

Q1 = 1'b1;

Q2 = 1'b1;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & !Q0 & !Q1 & !Q2 & Q3 & !UD) begin

// state 1110

Q0 = 1'b1;

Q1 = 1'b1;

Q2 = 1'b1;

Q3 = 1'b0;

end

else if (load & !ENP & !ENT & Q0 & !Q1 & !Q2 & Q3 & !UD) begin

// state 0001

Q0 = 1'b0;

Q1 = 1'b0;

Q2 = 1'b0;

Q3 = 1'b1;

end

end

end

If you look back at the 74x569, imagine that you just made a 1 bit and a 3 bit counter (trapped at 4) instead of the 4 bit binary counter. It would take very little additional logic to connect overflow/underflow (add to the 1 bit pass overflow to the clock for the 3 bit one to add - and - subtract from the 3 bit and pass the underflow to the 1 bit to subtract) and you would have a cute little easy to understand and maintain hdl... In general if you have lots of things involved in state changes withing a block, or lots of states withing a block... there is an easier way to design it. The important thing to remember is that there isn't a right or wrong way, there are always tradeoffs in size and speed and maintainability as well as style.

Counters are extremely useful, but I find that many times the design of the rest of the circuit gets adjusted to the counter that you want to use... while there are a number of variants with loading and triggering and signal passing capabilities, there isn't a great way to build a really universal module (which is why there are so many variations produced). If you are designing the hardware it's relatively easy to build the exact counter you need - but when you have to start adding more and more control logic before the counter you have available, it's easy to have timing problems at higher speeds.