T
Theo
Guest
gnuarm.deletethisbit@gmail.com wrote:
Types are good. But why do I have to write
'signal x : std_logic_vector(31 downto 0)' when I could just type
'logic [31:0] x' ?
I agree completely. But you can be explicit without hurting your keyboard.
Spend the keystrokes where it matters.
(I'm fed up of reading through pages and pages of Verilog code that merely
describes the interfaces to a component, which does a tiny amount of work and
then instantiates another component that's almost the same inside it. When
you get 8 levels down in this tree of input and output wires it gets very
tiresome)
So why is there a standard if tools can cherry pick what is and isn't
supported? One of my colleagues has a table of what SystemVerilog features
are supported across different tools. To have a multi-tool project, you end
up with the lowest common denominator, at which point most of the useful
features aren't an option.
> What about simulation? Is the BSV simulated or the Verilog?
BSV has its own simulator, which simulates at a higher level of abstraction
so runs a lot faster than a Verilog simulator. You can, of course, simulate
the verilog if you want to, but generally we find that code that simulates
in the BSV simulator will work first time on FPGA.
(The tricky bit is dealing with external IP like DRAMs - to get better
testing coverage we have randomised models in BSV, rather than simulating
the DRAM controller and DRAM IP in Modelsim)
> How do you distinguish code that is combinatorial?
function Bool i2xj( td i, td j )
provisos( Arith#(td), Eq#(td) );
return ( (i - 2*j) == 0 );
endfunction
is a polymorphic combinatorial function that takes two inputs i and j. We
don't know the type of i and j, but we do know that it must be the same, the
type (we'll call it 'td') must support arithmetic and testing equality. If
it doesn't, it's a compile error. So I couldn't use this for a string, for
example.
For any instantiation of this function, we'll accept the parameters i and j
and generate logic that computes i-2j and compares it to zero, returning a
boolean value.
If I wanted to instantiate this in a module, I'd do something like:
// define some registers with their types and default values
Reg#(UInt#(64)) f <- mkReg(44);
Reg#(UInt#(64)) g <- mkReg(22);
Reg#(Int#(9)) p <- mkReg(-1);
Reg#(Int#(9)) q <- mkReg(4);
Reg#(Bool) m <- mkReg(False);
Reg#(Bool) n <- mkReg(True);
rule comparethem;
// at the next clock cycle, set h to the combinatorial function of f and g
m <= i2xj(f, g);
// instantiate another copy of the function, this time with
// different types
n <= i2xj(p, q);
endrule
If you want your module to have purely combinational behaviour, there are
'Wire' types which act like registers but return the answer in the current
clock cycle, not the next one (unlike Verilog, a Wire has the same syntax as
a Reg, but different timing behaviour).
The compiler will schedule logic so that things that depend on Wire outputs
are downstream so that they are consumed when the wires have computed their
results. Wires are commonly used to pass data between rules (which are the
building block of BSV, each one being an atomic action with implicit
conditions).
Theo
I don't think that is the point. The verbosity of VHDL is intended.
There are things that you have to do in VHDL that aren't done in Verilog
where the intent is to avoid a class of mistakes. This is the reason for
strong typing which brings on some verbosity.
Types are good. But why do I have to write
'signal x : std_logic_vector(31 downto 0)' when I could just type
'logic [31:0] x' ?
an_integer_variable <= to_integer(some_other_data_type);
Sometimes the function to_integer() isn't defined for that "other" data
type and you have to write the conversion function or convert to an
intermediate type. The point is to not *assume* the compiler knows what
the user intended and to make it all explicit. Then a subsequent reader
can see exactly what was intended.
I agree completely. But you can be explicit without hurting your keyboard.
Spend the keystrokes where it matters.
(I'm fed up of reading through pages and pages of Verilog code that merely
describes the interfaces to a component, which does a tiny amount of work and
then instantiates another component that's almost the same inside it. When
you get 8 levels down in this tree of input and output wires it gets very
tiresome)
It's not quite that bad. The tools support what the users request. So
the most often used features are supported in most tools.
So why is there a standard if tools can cherry pick what is and isn't
supported? One of my colleagues has a table of what SystemVerilog features
are supported across different tools. To have a multi-tool project, you end
up with the lowest common denominator, at which point most of the useful
features aren't an option.
> What about simulation? Is the BSV simulated or the Verilog?
BSV has its own simulator, which simulates at a higher level of abstraction
so runs a lot faster than a Verilog simulator. You can, of course, simulate
the verilog if you want to, but generally we find that code that simulates
in the BSV simulator will work first time on FPGA.
(The tricky bit is dealing with external IP like DRAMs - to get better
testing coverage we have randomised models in BSV, rather than simulating
the DRAM controller and DRAM IP in Modelsim)
> How do you distinguish code that is combinatorial?
function Bool i2xj( td i, td j )
provisos( Arith#(td), Eq#(td) );
return ( (i - 2*j) == 0 );
endfunction
is a polymorphic combinatorial function that takes two inputs i and j. We
don't know the type of i and j, but we do know that it must be the same, the
type (we'll call it 'td') must support arithmetic and testing equality. If
it doesn't, it's a compile error. So I couldn't use this for a string, for
example.
For any instantiation of this function, we'll accept the parameters i and j
and generate logic that computes i-2j and compares it to zero, returning a
boolean value.
If I wanted to instantiate this in a module, I'd do something like:
// define some registers with their types and default values
Reg#(UInt#(64)) f <- mkReg(44);
Reg#(UInt#(64)) g <- mkReg(22);
Reg#(Int#(9)) p <- mkReg(-1);
Reg#(Int#(9)) q <- mkReg(4);
Reg#(Bool) m <- mkReg(False);
Reg#(Bool) n <- mkReg(True);
rule comparethem;
// at the next clock cycle, set h to the combinatorial function of f and g
m <= i2xj(f, g);
// instantiate another copy of the function, this time with
// different types
n <= i2xj(p, q);
endrule
If you want your module to have purely combinational behaviour, there are
'Wire' types which act like registers but return the answer in the current
clock cycle, not the next one (unlike Verilog, a Wire has the same syntax as
a Reg, but different timing behaviour).
The compiler will schedule logic so that things that depend on Wire outputs
are downstream so that they are consumed when the wires have computed their
results. Wires are commonly used to pass data between rules (which are the
building block of BSV, each one being an atomic action with implicit
conditions).
Theo