On this page you will find a number of MyHDL descriptions of flip-flops and latches.
Typically, you wouldn't describe flip-flops and latches as individual modules. Rather, they can be inferred from higher-level RTL description by a synthesis tool. However, as these circuits are small and widely known, they are well suited to explain basic MyHDL usage and to compare MyHDL with other solutions.
On this page, you will learn about the following MyHDL features and techniques:
The basic D flip-flop is a sequential device that transfers the value of the d input to the q output on every rising edge of the clock clk.
Here is the description of a D flip-flop in MyHDL:
from myhdl import * def dff(q, d, clk): @always(clk.posedge) def logic(): q.next = d return logic
Let's write a small test bench to simulate the design:
from random import randrange def test_dff(): q, d, clk = [Signal(bool(0)) for i in range(3)] dff_inst = dff(q, d, clk) @always(delay(10)) def clkgen(): clk.next = not clk @always(clk.negedge) def stimulus(): d.next = randrange(2) return dff_inst, clkgen, stimulus def simulate(timesteps): tb = traceSignals(test_dff) sim = Simulation(tb) sim.run(timesteps) simulate(2000)
Function test_dff creates an instance of the D flip-flop, and adds a clock generator and a random stimulus generator around it.
Function simulate simulates the test bench. Note how the MyHDL function traceSignals is used to create the test bench instance (instead of calling test_dff directly). As a result, a signal trace file will be created during simulation. This file can be inspecting in a waveform viewer. As a verification method, inspecting waveforms has its drawbacks, but it can be very effective to debug small designs.
Here's a screen shot of the simulation waveforms:
With MyHDL's toVerilog function, a D flip-flop instance can be converted to Verilog code:
def convert(): q, d, clk = [Signal(bool(0)) for i in range(3)] toVerilog(dff, q, d, clk) convert()
This is the resulting Verilog code:
module dff ( q, d, clk ); output q; reg q; input d; input clk; always @(posedge clk) begin: _dff_logic q <= d; end endmodule
One of the most useful sequential building blocks is a D flip-flop with an additional asynchronous reset pin. When the reset is not active, it operates as a basic D flip-flop as in the previous section. When the reset pin is active, the output is held to zero. Typically, the reset pin is active low.
Here is the description:
from myhdl import * def dffa(q, d, clk, rst): @always(clk.posedge, rst.negedge) def logic(): if rst == 0: q.next = 0 else: q.next = d return logic
Here is a test bench for the design:
from random import randrange def test_dffa(): q, d, clk, rst = [Signal(bool(0)) for i in range(4)] dffa_inst = dffa(q, d, clk, rst) @always(delay(10)) def clkgen(): clk.next = not clk @always(clk.negedge) def stimulus(): d.next = randrange(2) @instance def rstgen(): yield delay(5) rst.next = 1 while True: yield delay(randrange(500, 1000)) rst.next = 0 yield delay(randrange(80, 140)) rst.next = 1 return dffa_inst, clkgen, stimulus, rstgen def simulate(timesteps): tb = traceSignals(test_dffa) sim = Simulation(tb) sim.run(timesteps) simulate(20000)
Compared to the test bench for the basic D flip-flop, there is an additional reset generator, that generates reset pulses at random moments and with a random duration.
Here is a screen shot of the waveforms:
The design can be converted to Verilog as follows:
def convert(): q, d, clk, rst = [Signal(bool(0)) for i in range(4)] toVerilog(dffa, q, d, clk, rst) convert()
The result looks like this:
module dffa ( q, d, clk, rst ); output q; reg q; input d; input clk; input rst; always @(posedge clk or negedge rst) begin: _dffa_logic if ((rst == 0)) begin q <= 0; end else begin q <= d; end end endmodule
A basic latch is a sequential device with an input, and output and a control gate pin. When the gate is open, the output follows the input combinatorially. When it is closed, the output keeps its value.
The following code describes a latch:
from myhdl import * def latch(q, d, g): @always_comb def logic(): if g == 1: q.next = d return logic
Note the usage of the always_comb decorator. This is somewhat of a misnomer. (The name comes from a similar construct in SystemVerilog). It doesn't mean the generator describes a circuit that is necessarily combinatorial, but merely that it triggers whenever one of the input signals changes.
Here is a test bench to simulate the latch:
from random import randrange def test_latch(): q, d, g = [Signal(bool(0)) for i in range(3)] latch_inst = latch(q, d, g) @always(delay(7)) def dgen(): d.next = randrange(2) @always(delay(41)) def ggen(): g.next = randrange(2) return latch_inst, dgen, ggen def simulate(timesteps): tb = traceSignals(test_latch) sim = Simulation(tb) sim.run(timesteps) simulate(20000)
In addition to the latch instance, the test bench creates a random data generator for the input and for the controlling gate.
Here is a screen shot of the simulation waveforms:
We can convert the design as follows:
def convert(): q, d, g = [Signal(bool(0)) for i in range(3)] toVerilog(latch, q, d, g) convert()
Here is the result:
module latch ( q, d, g ); output q; reg q; input d; input g; always @(d, g) begin: _latch_logic if ((g == 1)) begin q <= d; end end endmodule
Note that when he toVerilog function converts the always_comb decorator, it infers which signals are used as inputs to the always block.