2019-08-21 16:14:33 -06:00
from abc import abstractproperty
from . . hdl import *
from . . build import *
__all__ = [ " IntelPlatform " ]
class IntelPlatform ( TemplatedPlatform ) :
"""
Required tools :
* ` ` quartus_map ` `
* ` ` quartus_fit ` `
* ` ` quartus_asm ` `
* ` ` quartus_sta ` `
The environment is populated by running the script specified in the environment variable
` ` NMIGEN_ENV_Quartus ` ` , if present .
Available overrides :
* ` ` nproc ` ` : sets the number of cores used by all tools .
* ` ` quartus_map_opts ` ` : adds extra options for ` ` quartus_map ` ` .
* ` ` quartus_fit_opts ` ` : adds extra options for ` ` quartus_fit ` ` .
* ` ` quartus_asm_opts ` ` : adds extra options for ` ` quartus_asm ` ` .
* ` ` quartus_sta_opts ` ` : adds extra options for ` ` quartus_sta ` ` .
Build products :
* ` ` * . rpt ` ` : toolchain reports .
* ` ` { { name } } . sof ` ` : bitstream as SRAM object file .
* ` ` { { name } } . rbf ` ` : bitstream as raw binary file .
"""
toolchain = " Quartus "
device = abstractproperty ( )
package = abstractproperty ( )
speed = abstractproperty ( )
suffix = " "
2019-11-30 17:07:48 -07:00
quartus_suppressed_warnings = [
10264 , # All case item expressions in this case statement are onehot
10270 , # Incomplete Verilog case statement has no default case item
10335 , # Unrecognized synthesis attribute
10763 , # Verilog case statement has overlapping case item expressions with non-constant or don't care bits
10935 , # Verilog casex/casez overlaps with a previous casex/vasez item expression
12125 , # Using design file which is not specified as a design file for the current project, but contains definitions used in project
18236 , # Number of processors not specified in QSF
292013 , # Feature is only available with a valid subscription license
]
2019-08-21 16:14:33 -06:00
required_tools = [
" quartus_map " ,
" quartus_fit " ,
" quartus_asm " ,
" quartus_sta " ,
]
file_templates = {
* * TemplatedPlatform . build_script_templates ,
" build_ {{ name}}.sh " : r """
# {{autogenerated}}
if [ - n " $ {{ platform._toolchain_env_var}} " ] ; then
QUARTUS_ROOTDIR = $ ( dirname $ ( dirname " $ {{ platform._toolchain_env_var}} " ) )
# Quartus' qenv.sh does not work with `set -e`.
. " $ {{ platform._toolchain_env_var}} "
fi
set - e { { verbose ( " x " ) } }
{ { emit_commands ( " sh " ) } }
""" ,
# Quartus doesn't like constructs like (* keep = 32'd1 *), even though they mean the same
# thing as (* keep = 1 *); use -decimal to work around that.
" {{ name}}.v " : r """
/ * { { autogenerated } } * /
{ { emit_verilog ( [ " -decimal " ] ) } }
""" ,
" {{ name}}.debug.v " : r """
/ * { { autogenerated } } * /
{ { emit_debug_verilog ( [ " -decimal " ] ) } }
""" ,
" {{ name}}.qsf " : r """
# {{autogenerated}}
{ % if get_override ( " nproc " ) - % }
set_global_assignment - name NUM_PARALLEL_PROCESSORS { { get_override ( " nproc " ) } }
{ % endif % }
{ % for file in platform . iter_extra_files ( " .v " ) - % }
set_global_assignment - name VERILOG_FILE " {{ file}} "
{ % endfor % }
{ % for file in platform . iter_extra_files ( " .sv " ) - % }
set_global_assignment - name SYSTEMVERILOG_FILE " {{ file}} "
{ % endfor % }
{ % for file in platform . iter_extra_files ( " .vhd " , " .vhdl " ) - % }
set_global_assignment - name VHDL_FILE " {{ file}} "
{ % endfor % }
set_global_assignment - name VERILOG_FILE { { name } } . v
set_global_assignment - name TOP_LEVEL_ENTITY { { name } }
set_global_assignment - name DEVICE { { platform . device } } { { platform . package } } { { platform . speed } } { { platform . suffix } }
{ % for port_name , pin_name , extras in platform . iter_port_constraints_bits ( ) - % }
set_location_assignment - to " {{ port_name}} " PIN_ { { pin_name } }
{ % for key , value in extras . items ( ) - % }
set_instance_assignment - to " {{ port_name}} " - name { { key } } " {{ value}} "
{ % endfor % }
{ % endfor % }
set_global_assignment - name GENERATE_RBF_FILE ON
""" ,
" {{ name}}.sdc " : r """
2020-02-06 16:37:15 -07:00
{ % for net_signal , port_signal , frequency in platform . iter_clock_constraints ( ) - % }
{ % if port_signal is not none - % }
create_clock - name { { port_signal . name } } - period { { 1000000000 / frequency } } [ get_ports { { port_signal . name } } ]
{ % else - % }
create_clock - name { { net_signal . name } } - period { { 1000000000 / frequency } } [ get_nets { { net_signal | hierarchy ( " | " ) } } ]
{ % endif % }
2019-08-21 16:14:33 -06:00
{ % endfor % }
""" ,
2019-11-30 17:07:48 -07:00
" {{ name}}.srf " : r """
{ % for warning in platform . quartus_suppressed_warnings % }
{ " " " " " " " {{ name}}.v " { } { } 0 { { warning } } " " 0 0 " Design Software " 0 - 1 0 " " }
{ % endfor % }
2020-02-06 16:37:15 -07:00
""" ,
2019-08-21 16:14:33 -06:00
}
command_templates = [
r """
2019-10-13 07:53:24 -06:00
{ { invoke_tool ( " quartus_map " ) } }
2019-08-21 16:14:33 -06:00
{ { get_override ( " quartus_map_opts " ) | options } }
- - rev = { { name } } { { name } }
""" ,
r """
2019-10-13 07:53:24 -06:00
{ { invoke_tool ( " quartus_fit " ) } }
2019-08-21 16:14:33 -06:00
{ { get_override ( " quartus_fit_opts " ) | options } }
- - rev = { { name } } { { name } }
""" ,
r """
2019-10-13 07:53:24 -06:00
{ { invoke_tool ( " quartus_asm " ) } }
2019-08-21 16:14:33 -06:00
{ { get_override ( " quartus_asm_opts " ) | options } }
- - rev = { { name } } { { name } }
""" ,
r """
2019-10-13 07:53:24 -06:00
{ { invoke_tool ( " quartus_sta " ) } }
2019-08-21 16:14:33 -06:00
{ { get_override ( " quartus_sta_opts " ) | options } }
- - rev = { { name } } { { name } }
""" ,
]
def add_clock_constraint ( self , clock , frequency ) :
super ( ) . add_clock_constraint ( clock , frequency )
# Make sure the net constrained in the SDC file is kept through synthesis; it is redundant
# after Quartus flattens the hierarchy and will be eliminated if not explicitly kept.
clock . attrs [ " keep " ] = 1
# The altiobuf_* and altddio_* primitives are explained in the following Intel documents:
# * https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/ug_altiobuf.pdf
# * https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/ug_altddio.pdf
# See also errata mentioned in: https://www.intel.com/content/www/us/en/programmable/support/support-resources/knowledge-base/solutions/rd11192012_735.html.
@staticmethod
def _get_ireg ( m , pin , invert ) :
def get_ineg ( i ) :
if invert :
i_neg = Signal . like ( i , name_suffix = " _neg " )
m . d . comb + = i . eq ( ~ i_neg )
return i_neg
else :
return i
if pin . xdr == 0 :
return get_ineg ( pin . i )
elif pin . xdr == 1 :
i_sdr = Signal ( pin . width , name = " {} _i_sdr " )
m . submodules + = Instance ( " $dff " ,
p_CLK_POLARITY = 1 ,
p_WIDTH = pin . width ,
i_CLK = pin . i_clk ,
i_D = i_sdr ,
o_Q = get_ineg ( pin . i ) ,
)
return i_sdr
elif pin . xdr == 2 :
i_ddr = Signal ( pin . width , name = " {} _i_ddr " . format ( pin . name ) )
m . submodules [ " {} _i_ddr " . format ( pin . name ) ] = Instance ( " altddio_in " ,
p_width = pin . width ,
i_datain = i_ddr ,
i_inclock = pin . i_clk ,
o_dataout_h = get_ineg ( pin . i0 ) ,
o_dataout_l = get_ineg ( pin . i1 ) ,
)
return i_ddr
assert False
@staticmethod
def _get_oreg ( m , pin , invert ) :
def get_oneg ( o ) :
if invert :
o_neg = Signal . like ( o , name_suffix = " _neg " )
m . d . comb + = o_neg . eq ( ~ o )
return o_neg
else :
return o
if pin . xdr == 0 :
return get_oneg ( pin . o )
elif pin . xdr == 1 :
o_sdr = Signal ( pin . width , name = " {} _o_sdr " . format ( pin . name ) )
m . submodules + = Instance ( " $dff " ,
p_CLK_POLARITY = 1 ,
p_WIDTH = pin . width ,
i_CLK = pin . o_clk ,
i_D = get_oneg ( pin . o ) ,
o_Q = o_sdr ,
)
return o_sdr
elif pin . xdr == 2 :
o_ddr = Signal ( pin . width , name = " {} _o_ddr " . format ( pin . name ) )
m . submodules [ " {} _o_ddr " . format ( pin . name ) ] = Instance ( " altddio_out " ,
p_width = pin . width ,
o_dataout = o_ddr ,
i_outclock = pin . o_clk ,
i_datain_h = get_oneg ( pin . o0 ) ,
i_datain_l = get_oneg ( pin . o1 ) ,
)
return o_ddr
assert False
@staticmethod
def _get_oereg ( m , pin ) :
2020-01-09 03:09:35 -07:00
# altiobuf_ requires an output enable signal for each pin, but pin.oe is 1 bit wide.
2019-08-21 16:14:33 -06:00
if pin . xdr == 0 :
2020-01-09 03:09:35 -07:00
return Repl ( pin . oe , pin . width )
2019-08-21 16:14:33 -06:00
elif pin . xdr in ( 1 , 2 ) :
oe_reg = Signal ( pin . width , name = " {} _oe_reg " . format ( pin . name ) )
oe_reg . attrs [ " useioff " ] = " 1 "
m . submodules + = Instance ( " $dff " ,
p_CLK_POLARITY = 1 ,
p_WIDTH = pin . width ,
i_CLK = pin . o_clk ,
i_D = pin . oe ,
o_Q = oe_reg ,
)
return oe_reg
assert False
def get_input ( self , pin , port , attrs , invert ) :
self . _check_feature ( " single-ended input " , pin , attrs ,
valid_xdrs = ( 0 , 1 , 2 ) , valid_attrs = True )
if pin . xdr == 1 :
port . attrs [ " useioff " ] = 1
m = Module ( )
m . submodules [ pin . name ] = Instance ( " altiobuf_in " ,
p_enable_bus_hold = " FALSE " ,
p_number_of_channels = pin . width ,
p_use_differential_mode = " FALSE " ,
i_datain = port ,
o_dataout = self . _get_ireg ( m , pin , invert )
)
return m
def get_output ( self , pin , port , attrs , invert ) :
self . _check_feature ( " single-ended output " , pin , attrs ,
valid_xdrs = ( 0 , 1 , 2 ) , valid_attrs = True )
if pin . xdr == 1 :
port . attrs [ " useioff " ] = 1
m = Module ( )
m . submodules [ pin . name ] = Instance ( " altiobuf_out " ,
p_enable_bus_hold = " FALSE " ,
p_number_of_channels = pin . width ,
p_use_differential_mode = " FALSE " ,
p_use_oe = " FALSE " ,
i_datain = self . _get_oreg ( m , pin , invert ) ,
o_dataout = port ,
)
return m
def get_tristate ( self , pin , port , attrs , invert ) :
self . _check_feature ( " single-ended tristate " , pin , attrs ,
valid_xdrs = ( 0 , 1 , 2 ) , valid_attrs = True )
if pin . xdr == 1 :
port . attrs [ " useioff " ] = 1
m = Module ( )
m . submodules [ pin . name ] = Instance ( " altiobuf_out " ,
p_enable_bus_hold = " FALSE " ,
p_number_of_channels = pin . width ,
p_use_differential_mode = " FALSE " ,
p_use_oe = " TRUE " ,
i_datain = self . _get_oreg ( m , pin , invert ) ,
o_dataout = port ,
2020-01-09 03:09:35 -07:00
i_oe = self . _get_oereg ( m , pin )
2019-08-21 16:14:33 -06:00
)
return m
def get_input_output ( self , pin , port , attrs , invert ) :
self . _check_feature ( " single-ended input/output " , pin , attrs ,
valid_xdrs = ( 0 , 1 , 2 ) , valid_attrs = True )
if pin . xdr == 1 :
port . attrs [ " useioff " ] = 1
m = Module ( )
m . submodules [ pin . name ] = Instance ( " altiobuf_bidir " ,
p_enable_bus_hold = " FALSE " ,
p_number_of_channels = pin . width ,
p_use_differential_mode = " FALSE " ,
i_datain = self . _get_oreg ( m , pin , invert ) ,
io_dataio = port ,
o_dataout = self . _get_ireg ( m , pin , invert ) ,
i_oe = self . _get_oereg ( m , pin ) ,
)
return m
def get_diff_input ( self , pin , p_port , n_port , attrs , invert ) :
self . _check_feature ( " differential input " , pin , attrs ,
valid_xdrs = ( 0 , 1 , 2 ) , valid_attrs = True )
if pin . xdr == 1 :
p_port . attrs [ " useioff " ] = 1
n_port . attrs [ " useioff " ] = 1
m = Module ( )
m . submodules [ pin . name ] = Instance ( " altiobuf_in " ,
p_enable_bus_hold = " FALSE " ,
p_number_of_channels = pin . width ,
p_use_differential_mode = " TRUE " ,
i_datain = p_port ,
i_datain_b = n_port ,
o_dataout = self . _get_ireg ( m , pin , invert )
)
return m
def get_diff_output ( self , pin , p_port , n_port , attrs , invert ) :
self . _check_feature ( " differential output " , pin , attrs ,
valid_xdrs = ( 0 , 1 , 2 ) , valid_attrs = True )
if pin . xdr == 1 :
p_port . attrs [ " useioff " ] = 1
n_port . attrs [ " useioff " ] = 1
m = Module ( )
m . submodules [ pin . name ] = Instance ( " altiobuf_out " ,
p_enable_bus_hold = " FALSE " ,
p_number_of_channels = pin . width ,
p_use_differential_mode = " TRUE " ,
p_use_oe = " FALSE " ,
i_datain = self . _get_oreg ( m , pin , invert ) ,
o_dataout = p_port ,
o_dataout_b = n_port ,
)
return m
def get_diff_tristate ( self , pin , p_port , n_port , attrs , invert ) :
self . _check_feature ( " differential tristate " , pin , attrs ,
valid_xdrs = ( 0 , 1 , 2 ) , valid_attrs = True )
if pin . xdr == 1 :
p_port . attrs [ " useioff " ] = 1
n_port . attrs [ " useioff " ] = 1
m = Module ( )
m . submodules [ pin . name ] = Instance ( " altiobuf_out " ,
p_enable_bus_hold = " FALSE " ,
p_number_of_channels = pin . width ,
p_use_differential_mode = " TRUE " ,
p_use_oe = " TRUE " ,
i_datain = self . _get_oreg ( m , pin , invert ) ,
o_dataout = p_port ,
o_dataout_b = n_port ,
i_oe = self . _get_oereg ( m , pin ) ,
)
return m
def get_diff_input_output ( self , pin , p_port , n_port , attrs , invert ) :
self . _check_feature ( " differential input/output " , pin , attrs ,
valid_xdrs = ( 0 , 1 , 2 ) , valid_attrs = True )
if pin . xdr == 1 :
p_port . attrs [ " useioff " ] = 1
n_port . attrs [ " useioff " ] = 1
m = Module ( )
m . submodules [ pin . name ] = Instance ( " altiobuf_bidir " ,
p_enable_bus_hold = " FALSE " ,
p_number_of_channels = pin . width ,
p_use_differential_mode = " TRUE " ,
i_datain = self . _get_oreg ( m , pin , invert ) ,
io_dataio = p_port ,
io_dataio_b = n_port ,
o_dataout = self . _get_ireg ( m , pin , invert ) ,
i_oe = self . _get_oereg ( m , pin ) ,
)
return m
# The altera_std_synchronizer{,_bundle} megafunctions embed SDC constraints that mark false
# paths, so use them instead of our default implementation.
def get_ff_sync ( self , ff_sync ) :
return Instance ( " altera_std_synchronizer_bundle " ,
p_width = len ( ff_sync . i ) ,
p_depth = ff_sync . _stages ,
i_clk = ClockSignal ( ff_sync . _o_domain ) ,
i_reset_n = Const ( 1 ) ,
i_din = ff_sync . i ,
o_dout = ff_sync . o ,
)
2020-03-08 15:37:40 -06:00
def get_async_ff_sync ( self , async_ff_sync ) :
2019-08-21 16:14:33 -06:00
m = Module ( )
2020-03-08 15:37:40 -06:00
sync_output = Signal ( )
if async_ff_sync . edge == " pos " :
m . submodules + = Instance ( " altera_std_synchronizer " ,
p_depth = async_ff_sync . _stages ,
i_clk = ClockSignal ( async_ff_sync . _domain ) ,
i_reset_n = ~ async_ff_sync . i ,
i_din = Const ( 1 ) ,
o_dout = sync_output ,
)
else :
m . submodules + = Instance ( " altera_std_synchronizer " ,
p_depth = async_ff_sync . _stages ,
i_clk = ClockSignal ( async_ff_sync . _domain ) ,
i_reset_n = async_ff_sync . i ,
i_din = Const ( 1 ) ,
o_dout = sync_output ,
)
m . d . comb + = async_ff_sync . o . eq ( ~ sync_output )
2019-08-21 16:14:33 -06:00
return m