Digital Module HDL

A project log for MagiLog: Open Automotive Datalogging

Developing a full featured datalogger on the cheap

nigelNigel 05/27/2018 at 23:280 Comments

Last weekend I was able to finish the HDL for digital inputs. The system comprises of 2 modules I’ve written and one pre-built IP. As with the other modules, each word of data will be 16 bits. I’ve chosen the following encoding scheme:

As you can see the MSB is the status bit, it is high when the input is high. The other 15 bits then are left to act as our counter. We won’t be directly calculating frequency/RPM, but the time between pulse events. As this means we need a division step we end up with nonlinearities in the resulting data. As we get closer to a single clock cycle, the read frequency gets very large and the gap between steps also grows. Because of this we need to carefully consider what clock rate we want to use as the timing input. After some playing around I ended up deciding on 250kHz. This gives me a max RPM of 18750 and a minimum of 457.8. The following graphs are cut off at 800 counts for readability. Also, the corresponding frequencies below 800 counts are higher than we probably would ever need.

Now onto the actual HDL I’ve used/written. The counter IP is configured as follows.

The digimodule module is the data collector for each channel, it times the gap between pulses and directly connect the pulse state to the state bit. Outside of the timer it’s pretty simple, there’s a one-shot pulse generator to clear the counter and another flag to denote when we’ve hit overflow.

`timescale 1ns / 1ps
// Company: MagiLog
// Engineer: Nigel Myers
// Create Date: 05/17/2018 03:25:31 PM
// Design Name: Digital Input'
// Module Name: DigiModule
// Project Name: MagiLog 
// Target Devices: 
// Tool Versions: 
// Description: 
// Dependencies: 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:

    module DigiModule(
    input wire signal, //Module I/O definitions
    input wire clock,
    output wire status,
    output reg [14:0] data
    wire [14:0] q; //Counter output wire 
    wire thresh; //Threshold notifier
    wire sclr; //Counter clear
    reg r1, r2, r3; //Single pulse registers
    reg flag; //overflow flag
    c_counter_binary_v11_0_0 DigiCount ( //Counter module
      .clk(clock), // input clk
      .sclr(sclr), // input sclr
      .thresh0(thresh), // output thresh0
      .q(q) // output [14 : 0] q
    assign status = signal; //Continously assign status state to signal level
    assign sclr = r2 && !r3; //Get one pulse out of sclr
    always @(posedge thresh or posedge sclr) begin //Overflow and clear routines
        if (thresh)
            flag = 1'b1;
        else if (sclr)
            flag = 1'b0;
            flag = flag;
    always @(posedge signal) begin //At every signal posedge, we check time since last pulse
        if (flag) begin
            data = 15'b111111111111111; //If data overflowed we use max value as notifier
        end else begin
            data = q;
    always @(posedge clock) begin //Pulse generator for sclr
        r1 <= signal;
        r2 <= r1;
        r3 <= r2;

The top-level module consists of a state machine with states for each channel, as well as an idle and send state. During the send state, we set the correct module output and associated address and pulse a write request to SRAM.

module DigitalMaster(
    input wire [9:0] SigIn, //Signals into module
    input wire trigger, clkSlow, clkFast,  //Record trigger, main clock, and timer clock
    output reg [15:0] dOut, //Data output
    output reg [3:0] adrOut, //Output address
    output reg write_en //Ram Write Enable
    wire [15:0] dataMux[9:0]; //Mux for counter module outputs
    reg [3:0] cstate; //State trackers
    reg [3:0] nstate;
    reg [3:0] nnstate;
    localparam [3:0]  //Channel addresses
        Ch0 = 4'b0000,
        Ch1 = 4'b0001,
        Ch2 = 4'b0010,
        Ch3 = 4'b0011,
        Ch4 = 4'b0100,
        Ch5 = 4'b0101,
        Ch6 = 4'b0110,
        Ch7 = 4'b0111,
        Ch8 = 4'b1000,
        Ch9 = 4'b1001,
        Send = 4'b1010,
        Idle = 4'b1011;
    DigiModule Chan0 ( //Instantiate 10 modules
    DigiModule Chan1 (

My next two deliverables are the CAN/OBD simulator PCB and the GPS parsing HDL. Each of these are more difficult than the last so I’m not sure if I’ll be able to keep on schedule. But I’ll try.