-
Notifications
You must be signed in to change notification settings - Fork 55
Digital Logic Basics
This page describes basic examples common to most HDLs. Ultimately these should not be very interesting.
First understand how you can translate between PipelineC global functions and regular HDL processes.
Consider the following generic VHDL:
-- Combinatorial logic with a storage register
signal the_reg : some_type_t;
signal the_wire : some_type_t;
process(input_wire, the_reg) is -- inputs sync to clk
variable input_variable: some_type_t;
variable the_reg_variable : some_type_t;
begin
input_variable := input_wire;
the_reg_variable := the_reg;
-- ... Do work with 'input_variable', 'the_reg_variable'
-- and other variables, functions, etc and it kinda looks like C ...
the_wire <= the_reg_variable;
end process;
the_reg <= the_wire when rising_edge(clk);
-- Connects output port
output <= the_wire;The equivalent PipelineC is
some_type_t the_reg;
some_type_t some_func_name(some_type_t input)
{
//... Do work with 'input', 'the_reg'
//... and other variables, functions, etc...
// Return connects output port
return the_reg;
}PipelineC functions are a single clock domain, rising edge assumed. Function arguments are input ports, the return value is the output port. Function bodies are combinatorial logic dataflow graphs.
For more information on modules check out this page.
These wires are contained within a single function/module/process. As in C, data flows from inputs to return, thus these wires are all unidirectional (typical C 'execution order'). These wires differ from the 'global clock crossing wires' and 'feedback wires discussed below.
...
{
// Assigning to local variables creates wires; standard C assignment behavior
// Data flows from function inputs to return
float x = y; // Wire from whatever is driving y to x
}//input_t input_reg;
//output_t output_reg;
output_t func(input_t in_wire)
{
static input_t input_reg; // Prefer static locals to globals
static output_t output_reg;
// Reading directly from a register and assigning it to the return value is an output reg
// Good practice to do at the start of function to ensure any future logic driving to output_reg
// is not what is going out the output port
output_t out_wire = output_reg;
// ... function combinatorial logic using output_reg and and input_reg variables as desired ...
// Assigning to a register reading directly from an input is an input reg
// Good practice to do the end of function to ensure any prior reads from input_reg
// are from the global/static state any not just a pass through of the in_wire
input_reg = in_wire;
return out_wire;
}uint1_t a_gate_example(uint1_t in0, uint1_t in1)
{
return in0 & in1; // Or, xor, etc
}uint32_t counter_reg;
uint32_t counter(uint1_t increment)
{
if(increment)
{
counter_reg += 1
}
return counter_reg;
}There is no unified global memory space as on a CPU.
Clock domain crossings are the general mechanism for moving data between main functions. See the documentation here.
In PipelineC signal flow is from inputs to return output. However, in digital logic it is often necessary to send signals in the opposite direction of data flow. FEEDBACK wires can be used to construct backwards flowing wires as described below.
Pseudo Code Example 1:
main(i)
{
bar_to_foo
#pragma FEEDBACK bar_to_foo
foo_to_bar = foo(i, bar_to_foo)
rv, bar_to_foo = bar(foo_to_bar)
return rv;
}Example 2:
uint32_t main(uint32_t x, uint32_t y)
{
uint32_t x_feedback;
#pragma FEEDBACK x_feedback
// This doesnt make sense/compile unless FEEDBACK pragma'd
// x_feedback has not been assigned a value
uint32_t x_looped_back = x_feedback;
// This last assignment to x_feedback is what
// shows up as the first x_feedback read value
x_feedback = x + 1;
return x_looped_back + y;
}These backwards flowing signals can be though of as leaving a function, outside of the function propagating backwards towards the inputs, and then reentering the function in a forward direction to be read like a normal wire (though carrying backwards propagated value).
Global non volatile clock domain crossing wires out and back into the same function are identical to these locally declared FEEDBACK wires (a clock crossing between the same clock domain == a wire).
C syntax isn't ideal hardware description language. There is a fair amount of autogenerated code to help with that.
Write raw HDL if you must.
Continue onto more examples like LEDs and UART examples on a real board.

