Counter Example

Here we will consider a component that implements an up counter in Rapid HDL.

Code

using System;
using System.Collections.Generic;
using System.Text;
using RapidHDL;

namespace RapidHDL
{
    public class UpCounter : Component 
    {

        public NodeVector ResetIn;
	public NodeVector CountOut;
        public NodeVector EnableIn;
        
        private ClockComponent ClockIn;

        int iResetValue;
        int iCountTo;
        int iWidth;
        int iCountBy;
        bool bRisingEdge;

        public UpCounter(Component poParentComponent, string psName,  ClockComponent poClockIn, int piWidth, bool pbRisingEdge, bool pbEnable, int piResetValue, int piCountTo, int piCountBy) 
            : base (poParentComponent,psName)
		{
            if (pbEnable)
                EnableIn = this.CreateNodeVector("ENABLE_I", 1);

            ResetIn = this.CreateNodeVector("RESET_I",1);            
            CountOut = this.CreateNodeVector("COUNT_O", piWidth);

            iWidth = piWidth;
            iResetValue = piResetValue;
            iCountBy = piCountBy;
            bRisingEdge = pbRisingEdge;
            iCountTo = piCountTo;
            //iCountTo = 0;

            ClockIn = poClockIn;
        }

        public override void GenerateStructure()
        {
            string sCountBy = Conversion.IntToBinaryString(iCountBy);
            if (sCountBy.Length > iWidth)
            {
                sCountBy = new string('1',iWidth);
                //should debug assert instead
            }

            string sCountTo = Conversion.IntToBinaryString(iCountTo);

            Constant oCountBy = new Constant(this, "count_by", sCountBy.Length, iCountBy);
            
            RegisterComponent oReg = new RegisterComponent(this, "count_reg",ClockIn,iWidth,bRisingEdge,(EnableIn != null));
            Adder oAdder = new Adder(this, "adder", iWidth,sCountBy.Length, false);
            ConstantSelect oConstantSelect = new ConstantSelect(this, "reset_to", iWidth, iResetValue);
            if (sCountTo != new String('1', iWidth) && Conversion.IntToBinaryString(iCountTo, iWidth) != new String('0', iWidth))
            {
                Constant oCountTo = new Constant(this, "count_to", sCountTo.Length, iCountTo);
                CompareEqual oLimit = new CompareEqual(this, "compare_limit", iWidth, sCountTo.Length);
                oLimit.InputNodesA.Connection  = oReg.OutputNodes;
                oLimit.InputNodesB.Connection  = oCountTo.OutputNodes;
                OrGate oOrReset = new OrGate(this, "reset_or", 2);
                oOrReset.InputNodes[0] = oLimit.Output[0];
                oOrReset.InputNodes[1] = ResetIn[0];
                oConstantSelect.SelIn[0] = oOrReset.OutputNodes[0];
            }
            else
            {
                oConstantSelect.SelIn[0] = ResetIn[0];
            }

            oAdder.InputNodesA.Connection  = oReg.OutputNodes;
            oAdder.InputNodesB.Connection  = oCountBy.OutputNodes;
            oReg.InputNodes.Connection  = oConstantSelect.OutNodes;
            oConstantSelect.InNodes.Connection  = oAdder.OutputNodes;
            CountOut.Connection = oReg.OutputNodes;
            oReg.EnableNodes.Connection = EnableIn;
        }


    }
}

IO

Our final component will have a reset signal input, an output count, and an enable input. The enable input will be optional, so it may or may not be enabled.

        public NodeVector ResetIn;
	public NodeVector CountOut;
        public NodeVector EnableIn;

These IO NodeVectors are instantiated in the constructor, so they can be used for interconnections with other components.

Constructor

The upcounter is instantiated with the following parameters:
  • Name - Instance name of the counter
  • ClockComponent - object representing the clocknet that will manage the counter's register. The TopLevel component has a main clock object already available.
  • Width - Output Width of the Counter
  • Rising Edge - flag set to true if the count changes on the rising edge, otherwise the count changes on the falling edge
  • Enable - If this flag is set to true, then the count only changes when the enable signal is high
  • Reset Value - The counter will be set to this value when the reset signal is applied.
  • Count To - The counter will wrap to zero when it reaches this limit.
  • Count By - The counter will increase by this amount at the clockedge

In the constructor, we initialize the node vectors that will be exposed for interconnections. However, we do not yet define the logic structure of the component.

 public UpCounter(Component poParentComponent, string psName,  ClockComponent poClockIn, int piWidth, bool pbRisingEdge, bool pbEnable, int piResetValue, int piCountTo, int piCountBy) 
            : base (poParentComponent,psName)
		{
            if (pbEnable)
                EnableIn = this.CreateNodeVector("ENABLE_I", 1);

            ResetIn = this.CreateNodeVector("RESET_I",1);            
            CountOut = this.CreateNodeVector("COUNT_O", piWidth);

            iWidth = piWidth;
            iResetValue = piResetValue;
            iCountBy = piCountBy;
            bRisingEdge = pbRisingEdge;
            iCountTo = piCountTo;
            //iCountTo = 0;

            ClockIn = poClockIn;
        }

Counter Structure

The virtual GenerateStructure() method should be overridden to script the structure of a container component. The counter is a container component because it does not have source or sink nodes, just passthrough nodes. The counter component will contain several children components.

        public override void GenerateStructure()
        {

First, the validity of the count-by value is checked against the width of the counter. If this value is too large for the counter, then it is set to 1. Perhaps an error should be raised instead...

            string sCountBy = Conversion.IntToBinaryString(iCountBy);
            if (sCountBy.Length > iWidth)
            {
                sCountBy = new string('1',iWidth);
                //should debug assert instead
            }

The Conversion class has many static methods to convert numbers to strings, and strings to numbers, using different numerica bases such as binary, decimal, and hex.

            string sCountTo = Conversion.IntToBinaryString(iCountTo);

The Constant component is simply a component with a single node vector that always outputs the same value. This is the first example of a parent component using a child component. The constructor for this constant inputs the parent class, the instance name, the bit width of the constant, and the constant value as an integer.

            Constant oCountBy = new Constant(this, "count_by", sCountBy.Length, iCountBy);

The register component acts like a flip-flop and will latch data on a rising clock edge or a falling clock edge. It may also be created with an optional Enable node. The constructor for the register inputs the parent class, an instance name, a Clock object, a bit width, a flag to indicate if the latch clock edge should be rising or falling, and a flag to indicate if the an enable node should be exposed.

Here, we create a register instance named "count_reg" to hold our counter state.

            RegisterComponent oReg = new RegisterComponent(this, "count_reg",ClockIn,iWidth,bRisingEdge,(EnableIn != null));

The adder component takes two inputs and produces a sum output. The 2 inputs may be of different widths, so both must be specified in the constructor. Here, the counter width is probably greater than the count by value width.

            Adder oAdder = new Adder(this, "adder", iWidth,sCountBy.Length, false);

The ConstantSelect component is like a special case multiplexer, used when one input to the mux is a constant. Because one input is a constant, the logic to implement the mux may be reduced. The constructor for the ConstantSelect component takes the parent component, an instance name, the width of the non-constant input, and the constant value.

            ConstantSelect oConstantSelect = new ConstantSelect(this, "reset_to", iWidth, iResetValue);

Next, the counter needs to determine how much logic is necessary to implement the "count to" limit of the counter. When the count reaches the "count to" limit, it will reset to zero. This logic requires a comparator at a minimum.

However, if the width of the counter is 2 bits, and if the counter counts to 3, then the counter will wrap automatically, and there is no need for comparator logic.

The "if" statement below determines if extra logic needs to be created to implement the "count to" limit.

            if (sCountTo != new String('1', iWidth) && Conversion.IntToBinaryString(iCountTo, iWidth) != new String('0', iWidth))
            {

When it is necessary to implement logic for the "count to" limit, the code below creates a constant for the "count_to" value.

                Constant oCountTo = new Constant(this, "count_to", sCountTo.Length, iCountTo);

A CompareEqual component inputs two values and outputs a high logic signal when the values are equal. Note, the widths of both inputs need to be specified in the constructor, because they may have different widths. If one input is shorter than another input, then the extra bits are compared to 0.

                CompareEqual oLimit = new CompareEqual(this, "compare_limit", iWidth, sCountTo.Length);

Next, we need to interconnect the public IO node vectors of the various components.

There are many syntaxes available to specify the connections between node vectors. In the example below, the Connection property of a NodeVector is assigned to a another NodeVector, which creates the connection. Here, the first input of the CompareEqual component is connected to the output of the counter register.

                oLimit.InputNodesA.Connection  = oReg.OutputNodes;

The second input of the CompareEqual component is connected to the "count to" limit constant.

                oLimit.InputNodesB.Connection  = oCountTo.OutputNodes;

Next, a 2 input Or Gate is defined that allow two signals to drive the reset node of the counter register.

                OrGate oOrReset = new OrGate(this, "reset_or", 2);

The Or gate has a single nodevector as the input. We need to connect individual nodes, to the gate, so we use the array indexor method of making a connection. The array index specifis which bit position of the NodeVector we are selecting. Assigning two nodes to each other causes them to connect. Here, the first input to the Or gate is the output of our CompareEqual comparator.

                oOrReset.InputNodes[0] = oLimit.Output[0];

The second input of the Or gate is the actual reset signal.

                oOrReset.InputNodes[1] = ResetIn[0];

Now, recall that the ConstantSelect is like a mux with a constant as one input. When the counter is reset or when the counter reaches it limt, it should latch in the constant "reset to" value. So, the next line of code causes the ouput of the constant select to change when the above Or gate signals that a reset is needed.

                oConstantSelect.SelIn[0] = oOrReset.OutputNodes[0];

Of course, if no "count to" logic is needed because the counter will automatically wrap around, then we only need to connect the reset signal to the constant select.

            }
            else
            {
                oConstantSelect.SelIn[0] = ResetIn[0];
            }

The following code completes the wiring together of the counter components.

The first input of the adder is the output of the register.

            oAdder.InputNodesA.Connection  = oReg.OutputNodes;

The second input of the adder is the output of the "count_by" register.

            oAdder.InputNodesB.Connection  = oCountBy.OutputNodes;

The input of the register is the output of the ConstantSelect (which is used to provide a reset value).

            oReg.InputNodes.Connection  = oConstantSelect.OutNodes;

The input of the ConstantSelect is the output of the adder.

            oConstantSelect.InNodes.Connection  = oAdder.OutputNodes;

The output of this component is the output of the count register.
            CountOut.Connection = oReg.OutputNodes;

And finally, the enable node of the register is connected to the enable input of this component.
            oReg.EnableNodes.Connection = EnableIn;
        }

Example Usage

The code below creates an up counter, with an enable signal that counts on the rising edge of the main clock. The counter resets to 1, increments by 1, and counts up to 27.

            UpCounter oUpCounter = new UpCounter(TopLevelComponent, "counter", 
                TopLevelComponent.MainClock, 5, true, true, 1, 27, 1);

Example Verilog

The Verilog module generated by the UpCounter is shown below.

module rhdl_UpCounter
   (RESET_I,
   ENABLE_I,
   count_reg_CLK_I,
   COUNT_O);

      input RESET_I;
      input ENABLE_I;
      input count_reg_CLK_I;

      output [4:0] COUNT_O;

      wire compare_limit_Out;
      wire reset_to_SEL_I;
      wire count_by_DATA_O;
      wire [4:0] reset_to_DATA_O;
      wire [4:0] compare_limit_InB;
      wire [5:0] adder_Out;

      wire [4:0] reset_to_DATA_I;



//*****  Redundant Wiring  *****

         assign reset_to_DATA_I = {adder_Out[4:0]};

//*****  Subcomponet Declarations  *****


rhdl_Constant3 count_by (
      .DATA_O({count_by_DATA_O})
   );

rhdl_ConstantSelect reset_to (
      .DATA_I({adder_Out[4:0]}),
      .SEL_I({reset_to_SEL_I}),
      .DATA_O({reset_to_DATA_O})
   );

rhdl_Adder adder (
      .InA({COUNT_O}),
      .InB({count_by_DATA_O}),
      .Out({adder_Out})
   );

rhdl_CompareEqual compare_limit (
      .InA({COUNT_O}),
      .InB({compare_limit_InB}),
      .Out({compare_limit_Out})
   );

rhdl_OrGate reset_or (
      .In({RESET_I,compare_limit_Out}),
      .Out({reset_to_SEL_I})
   );

rhdl_Constant4 count_to (
      .DATA_O({compare_limit_InB})
   );

rhdl_RegisterComponent count_reg (
      .ENABLE_I({ENABLE_I}),
      .CLK_I({count_reg_CLK_I}),
      .REG_I({reset_to_DATA_O}),
      .REG_O({COUNT_O})
   );

// Component Did Not Write Any Custom Verilog

endmodule

Other Generated Verilog

Other Verilog modules generated to support the UpConter are linked below.

Last edited Sep 9, 2009 at 9:04 PM by allen248, version 4

Comments

No comments yet.