SDL PreProcessor commands

SDL PreProcessor command consist of, the `Include command, the  `EXIT statement LOOP_DIRECTIVES and VARIABLE SUBSTITUTION commands. They greatly increase the efficiency of creating parts that contain multiple similar interfaces like a microprocessor with 2 DRAM busses or an FPGA with multiple IO Banks with the same number and types of pins.  They can also be used to quickly break up busses into nicely structured sub-busses 

Helpful Videos

Namelinklengthdescription
Using BUS_SLICERhttp://scast.cadenhance.com/bus-slicing-w-partbuilder11:44Different ways of working with bussed Pins using the SDL-Editor in PartBuilder
Working with Include Fileshttp://scast.cadenhance.com/using_sdl_include_files6:07Intro to the tools the SDL-Editor provides to create, insert, remove, replace and edit Include files
The Power of Replicate in SDLhttp://scast.cadenhance.com/watch/cb6fIsIAc56:30Intro to the workings of the Replicate Loop
Replicating FPGA Banks using SDL Editorhttp://scast.cadenhance.com/replicate_fpga_banks_with_sdl_editor7:43Shows how to wrap a block of sdl statements with a REPLICATE Loop

The Expanded SDL File

PartBuilder first scans the input SDL file to see if it contains  LOOP _DIRECTIVES or Variable Substitution. If it finds any, it creates a new, expanded version of the input SDL file which it saves to the local directory and then uses for the pin to symbol assigment step. The expanded file has all the LOOP_DIRECTIVES and VARIABLE_SUB_DIRECTIVES removed.  The LOOP_DIRECTIVES cause the expander to copy ranges of lines multiple times in the expanded file with the LOOP variables substituted. The expander function does not have any intelligence about the SDL statements inside the loop, it just makes copies and substitutes the  variable values.

One benefit of this approach is it allows the user to clearly see how the loops were expanded, and possibly catch any unintended consequences of the loops. While the user is encouraged to examine the expanded SDL File, They  should be careful not to edit it, as any changes they make to that file will be overwritten the next time PartBuilder reads and expands the input SDL file.

Include Files

Include files allow the user to create a library of common SDL functions for similar parts in a library. For instance, all of the members of the same FPGA family will usually have the same set of configuration pins, so the SDL to describe those pins can be created once, and then moved to a file called fpga_config_pins.sdl, which can then be included in the top level SDL file for all the parts in that family. The SDL Editor provides very efficient tools allowing the user to move lines of sdl into an include file, and to edit the contents of the include files. Another reason to use include files is to make the SDL for large parts much easier to read and edit, so the SDL for each symbol in a large part can be moved to its own SDL file and can be included in the top level SDL file. The SDL Editor tool is much more responsive when working with smaller SDL files so moving big sections of SDL files to include files will improve the users editing experience as well.

The Include directive is `Include incFileName

If a `INCLUDE fileName directive is found in the input SDL file, PartBuilder will perform a first pass expansion and insert the contents of the specified File into the expanded file.  PartBuilder can  handle multilevel inclusions (files included within include files), but there may not be too much need for that complexity. The 2 pass expansion process enables PartBuilder to handle include files which contain LOOP directives and VARIABLE substitution.

The incFileName can contain the whole path to the included file or just a fileName. If the full path is not specified, PartBuilder will first search the local working directory for the inlcuded file, if it does not find it there, it will search any directories listed in the CE_SDL_INCLUDE_DIRS config setting is a list of directories. This allows the user to create a Directory to store commonly used include files for easy reuse between parts.

Support for INCLUDE files was added in partBuilder version 18.4.2... a video demonstration of the new SDL_EDITOR support for INCLUDE files can be seen with this link: Video Demo

The `EXIT statement

The `EXIT pre-processor command can be added anywhere in the working SDL file to tell PartBuilder to stop processing any lines following that statement. This works great when the user wants to incrementally build symbols in the SDL. For instance, the SMART-Frac Feature builds a template file with all the pin-matches required to build the symbols, but it leaves a lot of pin_matches  with the CHOOSE_SIDE location modifier which tells the user that they need to pick a side to place the matching pins. PartBuilder errors out if it sees any pin_matches with the CHOOSE_SIDE modifier, so  putting an `EXIT command in before a list of pins that the user has not yet worked on,  allows the user to incrementally build symbols. (In this case the user should also select the BUILD PARTIAL SYMBOLS checkbox so that the symbol build process will run to completion even though PartBuilder knows that the user hasn't mapped all the pins yet) 

Support for the `EXIT statement was added in PartBuilder version v18.2.1



VARIABLE Value Assignment and Substitution

The user can use variables within Loops or just globally change a of a set of PIN_MATCHES in some MATCH_STATEMENTS that they cut and paste.

VARIABLES can be substituted anywhere in the SDL including in Comments, SYMBOL_NAME_DEFINITIONS and MATCH_STATEMENTS.

A Variable is assigned with the following statement:

`LET VAR_NAME=VALUE

The VAR_NAME cannot contain spaces, but it can consist of any other characters except the backtick and the equal sign.

The VALUE can be a number or a string or a mix of numbers and characters and spaces.

To have PartBuilder substitute the current Value of a variable in the SDL, the VARIABLE is prepended with a backtick and suffixed with 2 colons like:

`VAR_NAME:: 

Quick Example:

Input SDL with VariableExpanded SDL File after Variable Substitution
`LET SYM=3
RX_SYMBOL_`SYM::=
`LET A=[5:0]
LEFT:dpair=>RX`A::_P
RIGHT:dpair=>TX`A::_P

!BSS+2

`LET A=[11:6]
LEFT:dpair=>RX`A::_P
RIGHT:dpair=>TX`A::_P

;

RX_SYMBOL_3=
LEFT:dpair=>RX[5:0]_P
RIGHT:dpair=>TX[5:0]_P

!BSS+2

LEFT:dpair=>RX[11:6]_P
RIGHT:dpair=>TX[11:6]_P

;




LOOP_DIRECTIVES

There are 6 main types of LOOP_DIRECTIVES: 2 Types of LIST BASED LOOPS, REPEAT and BUS_SLICER LOOPS, REPLICATE LOOPS and ITERATIVE LOOPS

Each one must  start at the beginning of a line with the backtick ` character to tell PartBuilder that this is a special line

When PartBuilder Expands the Input SDL file upon seeing a LOOP_DIRECTIVE, what it really does is create multiple copies of the lines between

the `FOR and `END_FOR while substituting in the running  value of  the LOOP_VARIABLE as it copies

Lets show a simple example before we look at the technical parts

Simple Example of a LIST BASED LOOP

Input SDLexpanded SDL
Input SDL FILE
`FOR B in (1..2,15..16)
IO_BANK_`B::=
left:dpair=>clk`B::_i_p
right:dpair=>clk`B::_o_p
!BSS+2
left=>BUS`B::_RX_DQ[7:0]
right=>BUS`B::_TX_DQ[7:0]
;
`ENDFOR
Expanded SDL file
#This File has been expanded by partBuilder from the input file:symbolOrder.txt 
#Do not edit it as any changes you make will be lost the next time partBuilder
#runs the map_pins_to_symbols step

IO_BANK_1=
left:dpair=>clk1_i_p
right:dpair=>clk1_o_p
!BSS+2
left=>BUS1_RX_DQ[7:0]
right=>BUS1_TX_DQ[7:0]
;
IO_BANK_2=
left:dpair=>clk2_i_p
right:dpair=>clk2_o_p
!BSS+2
left=>BUS2_RX_DQ[7:0]
right=>BUS2_TX_DQ[7:0]
;
IO_BANK_15=
left:dpair=>clk15_i_p
right:dpair=>clk15_o_p
!BSS+2
left=>BUS15_RX_DQ[7:0]
right=>BUS15_TX_DQ[7:0]
;

IO_BANK_16=
left:dpair=>clk16_i_p
right:dpair=>clk16_o_p
!BSS+2
left=>BUS16_RX_DQ[7:0]
right=>BUS16_TX_DQ[7:0]
;


PartBuilder finds the `For in the input SDL file, so it goes through the process of creating the expanded file

It first prints the warning at the top of the file

The Loop is started with `FOR B in (1..2,15..16) at line 1 and ends with the `ENDFOR at line 9

The IO_BANK`B::= SYMBOL_NAME_DEFINTION which starts at line 2 and ends with the semicolon at line 7

Part Builder expands the (1..2,15..16) to a list of 1,2,15,16, and goes through the loop 4 times, assigning variable B the next value in the list.

for each iteration of the loop, it prints all the lines including the space after the ';' to the expanded file while substituting the current value of B for that

iteration.





LIST BASED LOOPS:

These loops assign the LOOP_VAR or LOOP_VAR_LIST with consecutive values from the provided LIST_OF_VALUES. 

Single Variable List based Loop


`FOR LOOP_VAR IN (LIST_OF_VALUES)

SDL_STATEMENTS (some containing `LOOP_VAR:: for LOOP_VARIABLE substitution )

`END_FOR (or `ENDFOR)

The LOOP_VAR is used to store the individual values from the LIST_OF_VALUES. It needs to start with a letter from A-Z. There is no restriction on how long the LOOP_VAR name is 

The LIST_OF_VALUES is very flexible It is built of comma separated elements which can be numbers, letters, words, or a RANGE_OPERATOR ('1..11' or '7..3')

PartBuilder reads the LIST_OF_VALUES (and expands any range_operators)  

The '!UNDEF'  keyword  will be substituted for any Empty elements in the LIST_OF_VALUES


It then makes one copy of the enclosed SDL_STATEMENTS for each value in the expanded list of values.

Examples:

LOOPexpanded to:notes
`FOR B IN (2,3,5,7,9) 
left=>DQ`B::
`END_FOR
left=>DQ2
left=>DQ3
left=>DQ5
left=>DQ7
left=>DQ9


`FOR IO_BANK IN (1..4,,B1,15..17,21..19)
right=>IO_*_`IO_BANK::
`END_FOR
right=>IO_*_1
right=>IO_*_2
right=>IO_*_3
right=>IO_*_4
right=>IO_*_!UNDEF
right=>IO_*_B1
right=>IO_*_15
right=>IO_*_16
right=>IO_*_17
right=>IO_*_21
right=>IO_*_20
right=>IO_*_19

The LIST_OF_VALUES contains multiple single elements mixed with several range operatators, and one missing value 

It will be expanded to:

1,2,3,4,!UNDEF,B1,15,16,17,21,20,19

which has 12 elements.

note the last range operator 21..19 was specified in MSB..LSB order

PartBuilder makes 12 copies of the MATCH_STATEMENT, substituting the value of IO_BANK each time






MultiVariable List based Loop

`FOR LOOP_VAR_LIST IN (LIST_OF_VALUES

SDL_STATEMENTS (including `LOOP_VAR:: for substitution )

`END_FOR (or `ENDFOR)

The LOOP_VAR_LIST is used to pull more than one value from the LIST_OF_VALUES. It is a comma separated list of LOOP_VARS

The LIST_OF_VALUES is built of comma separated elements which can be numbers, letters, words, or a RANGE OPERATOR ('1..11' or '7..3')

PartBuilder reads the LIST_OF_VALUES (and expands any RANGE_OPERATORS)

It assigns the first n elements  of the LIST_OF_VALUES to the variables in the LOOP_VAR_LIST

It then makes one copy of the enclosed SDL_STATEMENTS for each n elements in the expanded list of values.



Examples:

LoopExpanded toNotes

`For SNAME,RST_NUM IN (A,2,B,4)
SYM_`SNAME::=
right:bubble=>reset`RST_NUM::_n
;
`END_FOR

`For SNAME,RST_NUM IN (A,2,B,4)

SYM_A=
right:bubble=>reset2_n
;
SYM_B=
right:bubble=>reset4_n
;


Part Builder pulls 2 elements at a time out of the LOOP_VAR_LIST to populate

LOOP_VARS SNAME and RST_NUM

`For OB,EB IN (1..2,7..8)
IO_BANKS_`OB::_`EB::=
left=>IO_*_`OB::
right=>IO_*_`EB::
;
`END_FOR

IO_BANKS_1_2=
left=>IO_*_1
right=>IO_*_2
;
IO_BANKS_7_8=
left=>IO_*_7
right=>IO_*_8
;

PartBuilder expands the LIST_OF_VALUES to 1,2,7,8

it assigns 1,2 to OB and EB and then copies the lines

`For OB,EB IN (1..2,7..8,9)
IO_BANKS_`OB::_`EB::=
left=>IO_*_`OB::
right=>IO_*_`EB::
;
`END_FOR

IO_BANKS_1_2=
left=>IO_*_1
right=>IO_*_2
;
IO_BANKS_7_8=
left=>IO_*_7
right=>IO_*_8
;
IO_BANKS_9_!UNDEF=
left=>IO_*_9
right=>IO_*_!UNDEF
;

Very similar to previous, but there is only one element left in the list for the 3rd pass of the copy

You might be building a part with 5 banks, so the first 2 symbols will have an odd and even bank, but

the last symbol will only have one.

PartBuilder will gracefully handle this case for you in 2 steps...

For the 3rd symbol, PartBuilder expansion replaces the empty EB variable with !UNDEF

When PartBuilder reads the expanded SDL and sees the !UNDEF in the SYMBOL_NAME_DIRECTIVE,

it removes the !UNDEF and the Trailing underscore from the symbol_name.

So it ends up with a symbol named IO_BANKS_9

any MATCH_STATEMENT containing an !UNDEF will be removed from the list since it can't match anything

`For OB,EB IN (1..2,3,!UNDEF,7..8)
IO_BANKS_`OB::_`EB::=
left=>IO_*_`OB::
right=>IO_*_`EB::
;
`END_FOR

IO_BANKS_1_2=
left=>IO_*_1
right=>IO_*_2
;

IO_BANKS_3_!UNDEF=

left=>IO_*_3

right=>IO_*_!UNDEF

:


IO_BANKS_7_8=
left=>IO_*_7
right=>IO_*_8
;

Manually Adding the !UNDEF is very powerful as well

In this case there is no IO_BANK_4 to pair with IO_BANK_3 so  we manually insert an !UNDEF where bank 4 should have been

Again PartBuilder will gracefully handle this case for you 

For the 2nd symbol, PartBuilder expansion uses the 3 and the  !UNDEF values 

When PartBuilder reads the expanded SDL and sees the !UNDEF in the SYMBOL_NAME_DIRECTIVE, and in the pin_matches

it removes the !UNDEF and the Trailing underscore from the symbol_name.

and removes the !UNDEF matches from the list without reporting any warnings

IO_BANKS 7 and 8 get grouped together properly


List of Multiple Lists... New after v21.2.2

`For OB,EB IN (1,3,!UNDEF,5,7) (2..8:2)
IO_BANKS_`OB::_`EB::=
left=>IO_*_`OB::
right=>IO_*_`EB::
;
`END_FOR

The next lines are equivalent, The loop expander will substitute !UNDEF for the missing value between commas

`For OB,EB IN (1,3,,5,7) (2..8:2)
IO_BANKS_`OB::_`EB::=
left=>IO_*_`OB::
right=>IO_*_`EB::
;
`END_FOR

IO_BANKS_1_2=
left=>IO_*_1
right=>IO_*_2
;

IO_BANKS_3_!UNDEF=

left=>IO_*_3

right=>IO_*_!UNDEF

:


IO_BANKS_7_8=
left=>IO_*_7
right=>IO_*_8
;

In the above examples, the user was responsible for interleaving the values of the lists so that the numbers would be pulled out in the proper order

in this new syntax, we added support to let the user create multiple lists that will be used to assign values to each variable which is easier to do 

So the first list will produce 1,3,!UNDEF,5,7 and those values will be assigned to the OB variable

And values from the 2nd list (2..8:2)

which will produce the list 2,4,6,8 when expanded

These values will be assigned to the EB variable

The user must add a list for each comma separated variable in the first list

`for var1,var2,var3,...varN in (List1) (List2) (List3),...(ListN)

if any of the lists are missing elements of the longest list, those values will be automatically populated with !UNDEF


`FOR side,bank_name,bank_type (left,right,right,top) (bank1,bank7,bank11,bank25) (user,user,none,admin)

`side::=>`bank_type::_`bank_name

`END_FOR

left=>user_bank1

right=>user_bank7

right=>none_bank11

top=>admin_bank25

Here is a more interesting application for using a separate list for each variable

Its easier to separate these elements into 3 lists

instead of having to write a list like this (left,bank1,user,right,bank7,user,right,bank11,none,top,bank25,admin)



BUS_SLICER LOOPS (Added in PartBuilder Version 20.5.1a)

BUS_SLICER LOOPS are a special case of the REPEAT LOOPS where the user does not need to modify the PIN_MATCH to split up existing bussed pins into smaller organized slices.

`BUS_SLICER n 

SDL_STATEMENTS 

`END_BUS_SLICER

Operation:

The BUS_SLICER Loop provides an easy way to arrange and distribute larger busses and groups of busses

Each pass through the loop automatically adds a slice of a larger bus
It calculates the size of the slice by dividing the (number of bits in the bus)/(the number of Loops). 

It selects whether the indicies will be incremented or decremented  by comparing  the MSB and LSB  of the Specified bus-range in each pinMatch

Use Case:

Smart-FRAC attempts to combine related sets of pins into busses to minimize the number of pinMatches the user has to work with to complete their symbols.

In some cases, The user will want to split out the pins of those busses in a more distributed fashion.

For instance, When SMART-FRAC finds a part with 3 I2C busses having pins

I2C_SDA_0, I2C_SCL_0, I2C_ALERT_N_0, I2C_SDA_1, I2C_SCL_1, I2C_ALERT_N_1 and  I2C_SDA_2, I2C_SCL_2, I2C_ALERT_N_2,

It will combine those pins into 3 separate bussed pinMatches I2C_SDA_[0:2], I2C_SCL_[0:2] and I2C_ALERT_[0:2].

If Those pinMatches are used untouched, the I2C busses for the part 

The user would probably prefer to interleave the pins by the bus number 0, 1 and 2.

They can interleave these busses manually by splitting up the busses in the SDL-EDITOR (the new EXPAND_BUS operation can help)

The BUS_SLICER provides an automated way of doing this, and allows the user to easily configure the spacing between pins and change it in only one place instead of in 3 places if they want to fix it


BUS_SLICER Examples

BUS_SLICER LOOPAFTER EXPANSIONNOTES:

I2C_BUS_PINS

`BUS_SLICER 3


LEFT=>I2C_SDA[2:0]

LEFT=>I2C_SCL[2:0]

RIGHT=>I2C_ALERT[2:0]_N
!BALANCE_SYMBOL_SIDES+1


`END_BUS_SLICER
; #end symbol DDR_DQ_PINS

I2C_BUS_PINS=

LEFT=>I2C_SDA2

LEFT=>I2C_SCL2

RIGHT=>I2C_ALERT2_N
!BALANCE_SYMBOL_SIDES+1

LEFT=>I2C_SDA1

LEFT=>I2C_SCL1

RIGHT=>I2C_ALERT1_N
!BALANCE_SYMBOL_SIDES+1

LEFT=>I2C_SDA0

LEFT=>I2C_SCL0

RIGHT=>I2C_ALERT0_N
!BALANCE_SYMBOL_SIDES+1


;#end symbol I2C_BUS_PINS

The BUS_SLICER count is 3, so the expansion will create 3 copies of whats between the `BUS_SLICER and `END_BUS_SLICER pre-processor statements

The  3 pin-matches I2C_SDA[2:0], I2C_SCL[2:0]  and I2C_ALERT[2:0]_N will be expanded in a case by case basis

In all cases the Starting range digit 2, is greater than the ending range digit 0. so The expander will decrement the expanded indicies from 2 down to 0 

The end result is that the 3 pins for each I2C busses are placed together, rather than placing the 3 bussed pins together

Note that the BALANCE_SYMBOL_SIDES statements do not have recognizeable  bus indicies so they are repeated with no modification each pass through the REPEAT LOOP


DQ_BUS_PINS=

`BUS_SLICER 4 

RIGHT=>DQ[31:0] 
RIGHT=>SPACERx1
RIGHT:dpair=>DQS_P[3:0] 
RIGHT=>SPACERx1

`END_BUS_SLICER

; #end DQ_BUS_PINS symbol

DQ_BUS_PINS=

RIGHT=>DQ[31:24]

RIGHT=>SPACERx1

RIGHT=>DQS_P3

RIGHT=>SPACERx1


RIGHT=>DQ[23:16]

RIGHT=>SPACERx1

RIGHT=>DQS_P2

RIGHT=>SPACERx1


RIGHT=>DQ[15:8]

RIGHT=>SPACERx1

RIGHT=>DQS_P1

RIGHT=>SPACERx1


RIGHT=>DQ[7:0]

RIGHT=>SPACERx1

RIGHT=>DQS_P0

RIGHT=>SPACERx1


#slice all lines between here and END_BUS_SLICER 4 times

#PartBuilder calculates the slice_size to be 8 bits since  its slicing a 32 bit bus 4 times
#it will decrement because the Bus is ordered High To Low
#so the first slice will be [31:28]
#and the following loops will be: [27:24],[23:20],...[3:0]

#since there is no bus index in the RIGHT=>SPACERx1 this will be duplicated for each loop and  you will get a spacer between each 4 bit  DQ slice and the 1 bit DQS bus slice

#slice size for the DQS_P[7:0} bus is 1 (interleaving the 8 bit bus 8 times)

#It will starts at DQS_P[7] decrements 6,5,4,3,2,1,0



REPEAT  LOOPS:

REPEAT_LOOPS provide a convenient way to split up bussed signals and their associated control signals into nicely grouped slices.

The LOOP_VAR in a repeat loop is implied in the SLICE_IDX or IDX  of the BUS INC_DEC Operator

`REPEAT n 

SDL_STATEMENTS (some with BUSSED INC_DEC operators)

`END_REPEAT

Version 20.5.1a added support for simpler REPEAT  Indicies 

Customers found it difficult to edit the bussesd pinMatches to meet the legacy syntax with the BUS_NAME[SLICE_IDX!(INC|DEC)_COUNT] 

Version 20.5.1a added support for a newer simpler syntax in the PIN_MATCHES wrapped by the `REPEAT Command 

BUS_NAME[DIG1:DIG2:SLICE_COUNT] 

PartBuilder automatically determines if the slice operation should Increment or decrement by looking at the first 2 DIGITS DIG1 and DIG2 to see which is greater

Simple REPEAT_LOOP Example

REPEAT LOOPAFTER EXPANSIONNOTES:

#increment on left

#decrement on right

DDR_DQ_PINS=
`REPEAT 4

LEFT=>DQ[32:63:8]

RIGHT=>DQ[31:0:8]
!BALANCE_SYMBOL_SIDES+1

LEFT:dpair=>DQS_P[4:7:1] RIGHT:dpair=>DQS_P[3:0:1]

!BALANCE_SYMBOL_SIDES+1

`END_REPEAT
; #end symbol DDR_DQ_PINS

DDR_DQ_PINS=

LEFT=>DQ[32:39]
RIGHT=>DQ[31:24]
!BALANCE_SYMBOL_SIDES+1

left:dpair=>DQS_P4
right:dpair=>DQS_P3

!BALANCE_SYMBOL_SIDES+1

LEFT=>DQ[40:47]
RIGHT=>DQ[32:16]
!BALANCE_SYMBOL_SIDES+1

LEFT:dpair=>DQS_P5
RIGHT:dpair=>DQS_P2

!BALANCE_SYMBOL_SIDES+1

LEFT=>DQ[48:55]
RIGHT=>DQ[15:8]
!BALANCE_SYMBOL_SIDES+1

LEFT:dpair=>DQS_P6
RIGHT:dpair=>DQS_P1

!BALANCE_SYMBOL_SIDES+1

LEFT=>DQ[56:63]
RIGHT=>DQ[7:0]
!BALANCE_SYMBOL_SIDES+1

LEFT:dpair=>DQS_P7
RIGHT:dpair=>DQS_P0

!BALANCE_SYMBOL_SIDES+1


;#end symbol DDR_DQ_PINS

PartBuilder processes the  4 pin-matches DQ[32:63:8], DQS_P[4:7:1], DQ[31:0:8]  and DQS_P[3:0:1] pin-matches separately

DQ[32:63:8]

The SLICE_COUNT is 8 and tells PartBuilder to slice the DQ pin Match by 8 each pass through the REPEAT operation. 32 is less than 63, so the Digits will be incremented  and the loop will create the following 8-bit slices  DQ[32:39],DQ[40:47],DQ[48:55] and DQ[56:63]

DQ[31:0:8]

The SLICE_COUNT is:8 tells PartBuilder to slice the DQ pin Match by 8 each pass through the REPEAT operation. 31 is greater than 0, so the Digits  will be decremented and the loop will create the following 8-bit slices  DQ[31:24],DQ[23:16],DQ[15:8] and DQ[7:0]

DQS_P[4:7:1]

The SLICE_COUNT is:1 and tells PartBuilder to slice the DQS_P pin Match by 1 each pass through the REPEAT operation. 4 is less than 7, so the Digits will be incremented   and the loop creates the following one bit slices: DQS_P4, DQS_P5, DQS_P6, DQS_P7 

DQS_P[3:0:1]

The SLICE_COUNT is:1 and tells PartBuilder to slice the DQS_P pin Match by 1 each pass through the REPEAT operation. 3 is greater than 0, so the digits will be decremented and the loop creates the following one bit slices: DQS_P3, DQS_P2, DQS_P1, DQS_P0


Note that the BALANCE_SYMBOL_SIDES statements do not have recognizeable  bus indicies so they are repeated with no modification each pass through the REPEAT LOOP





There are some limitations to the simpler syntax... the MSB:LSB:SLICE_COUNT will only be sliced in one direction... MSB->LSB or LSB->MSB 

With that in mind, the results of the BUS INC_DEC Operator shown below cannot be reproduced with the newer syntax, because the SLICES below are entered MSB down to LSB but the slices increment from 7:0->15:8→23:16->31:24



LEGACY REPEAT_LOOP Example  with BUS INC_DEC Operators

BUS INC_DEC Operators

BUS_NAME[SLICE_IDX!(INC|DEC)_COUNT] 

The BUS_SLICE is used to create smaller slices of a larger bus. The SLICE IDX is a ':' separated range that provides the First and Last digits of the first slice you want to create

The COUNT appended to the INC|DEC operator is added to both elements to create the next slice at the end of each iteration of the REPEAT Loop


BUS_NAME[IDX:(INC|DEC)_COUNT]

The BUS_NAME is used to create one element for a signal in a control bus. The IDX is a single number and it is the starting element of the control bus you are slicing

The COUNT appended to the INC|DEC operator is added to the IDX select the next bus_element at the end of each iteration of the REPEAT Loop

REPEAT_LOOP Example  with BUS INC_DEC Operators

repeat loopafter expansionnotes

DDR_DQ=
`REPEAT 4
right=>DQ[7:0!INC_8]
right=>spacer
right:dpair=>DQS_P[0!INC_1]
`END_REPEAT
;

DDR_DQ=
right=>DQ[7:0]
right=>spacer
right:dpair=>DQS_P0
right=>DQ[15:8]
right=>spacer
right:dpair=>DQS_P1
right=>DQ[23:16]
right=>spacer
right:dpair=>DQS_P2
right=>DQ[31:24]
right=>spacer
right:dpair=>DQS_P3
;

Modern DDR DRAM interfaces typically have a differential strobe associated with 4,8 or 16 bit slices of a data bus

In this example the first strobe is: DQS0 for 8 bit data slice DQ[7:0]

The next strobe is DQS1 for 8 bit data slice DQ[15:8]

The next strobe is DQS2 for 8 bit data slice DQ[23:16]

The next strobe is DQS3 for 8 bit data slice DQ[31:24]

Note that the `REPEAT loop was located inside the DDR_DQ= SYMBOL_NAME_DEFINITION



REPLICATE LOOPS:

The REPLICATE LOOP was released in v2.9.10 of PartBuilder to make it easier to define one complete symbol and then wrap a REPLICATE directive around it to create other symbols based off the first one. It saves the user the trouble of having to create a loop variable like A  and then substitute the `A:: into the pin_matches. It gives the user an easy way to replicate symbols (or any block of SDL) without having to think too much like a programmer

`REPLICATE SUBSTITUTION_DEFINITION (SEPARATOR [SUB_DEF]...SEPARATOR [SUB_DEF])

SDL_STATEMENTS with a common match 

`END_REPEAT

SUBSTITUTION_DEFINITIONS 

The user provides the SUBSTITUTION_DEFINITION (SUB_DEF) to tell partBuilder what to change within in the SDL wrapped by the `REPLICATE directive each time through the REPLICATE loop. Multiple SUB_DEFS can be provided in the same `REPLICATE statement... they are separated by a space, semicolon or colon. A SUB_DEF is created with a 

match_string=>replace_list


The replace_list is a comma separated list of replacement strings.

The Replicate Loop will be run once for each of the strings in the replace_list

If the SUB_DEF is P0_=>P0_,P1_,P2_,P3_  the replicate Loop will be executed 4 times, once for P0_, P1_,P2_and P3_

The replace_list can also be provided using range operators so that P0=>P(0..3)_ gets expanded to P0=>P0_,P1_,P2_,P3_

If multiple SUB_DEFS are provided, each one must have the same number of items in their replace_list 

`REPLICATE Expansion Example

REPLICATE loopafter expansionNotes
`REPLICATE P0_=>P0_,P1_,P2_,P3_
P0_SYMBOL=
LEFT:dpair=>P0_RXP           #(2 pins)
RIGHT:dpair=>P0_TXP           #(2 pins)
!BALANCE_SYM_SIDES+2#
RIGHT=>P0_RXD[7:0]           #(8 pins)
LEFT=>P0_TXD[7:0]           #(8 pins)
!BALANCE_SYM_SIDES+1#
RIGHT=>P0_COL           #(1 pins)
RIGHT=>P0_CRS           #(1 pins)
;
`END_REPLICATE
P0_SYMBOL=
LEFT:dpair=>P0_RXP           #(2 pins)
RIGHT:dpair=>P0_TXP           #(2 pins)
!BALANCE_SYM_SIDES+2#
RIGHT=>P0_RXD[7:0]           #(8 pins)
LEFT=>P0_TXD[7:0]           #(8 pins)
!BALANCE_SYM_SIDES+1#
RIGHT=>P0_COL           #(1 pins)
RIGHT=>P0_CRS           #(1 pins)
;
P1_SYMBOL=
LEFT:dpair=>P1_RXP           #(2 pins)
RIGHT:dpair=>P1_TXP           #(2 pins)
!BALANCE_SYM_SIDES+2#
RIGHT=>P1_RXD[7:0]           #(8 pins)
LEFT=>P1_TXD[7:0]           #(8 pins)
!BALANCE_SYM_SIDES+1#
RIGHT=>P1_COL           #(1 pins)
RIGHT=>P1_CRS           #(1 pins)
;
P2_SYMBOL=
LEFT:dpair=>P2_RXP           #(2 pins)
RIGHT:dpair=>P2_TXP           #(2 pins)
!BALANCE_SYM_SIDES+2#
RIGHT=>P2_RXD[7:0]           #(8 pins)
LEFT=>P2_TXD[7:0]           #(8 pins)
!BALANCE_SYM_SIDES+1#
RIGHT=>P2_COL           #(1 pins)
RIGHT=>P2_CRS           #(1 pins)
;
P3_SYMBOL=
LEFT:dpair=>P3_RXP           #(2 pins)
RIGHT:dpair=>P3_TXP           #(2 pins)
!BALANCE_SYM_SIDES+2#
RIGHT=>P3_RXD[7:0]           #(8 pins)
LEFT=>P3_TXD[7:0]           #(8 pins)
!BALANCE_SYM_SIDES+1#
RIGHT=>P3_COL           #(1 pins)
RIGHT=>P3_CRS           #(1 pins)
;
the `REPLICATE P0_=>P0_,P1_,P2_,P3_  directive 
tells partBuilder to make 4 copies of the symbol.
and as it makes the copies, it will substitute any occurrence of 
P0_ for P0_ (on the first pass, so it doesn't really change)
then 
P1_ for P0_
P2_ for P0_
and finally
P3_ for P0_

The `REPLICATE loop eliminates the need to do the variable substitution step
usually required for a list_based or iterative loop.
Instead, you  can create the PORT_P0 symbol, tweak it until you like it,
and then wrap it with the `REPLICATE/`END_REPLICATE command.

PartBuilder will then create 4 identical symbols for 
P0_SYMBOL, P1_SYMBOL, P2_SYMBOL and P3_SYMBOL

If you had an 8 port device, you would just change the SUB_DEF to
P0_=>P(0..7)_ and PartBuilder will expand the indices and create the
8 identical symbols.
`REPLICATE _A=>_A,_C,_E _B=>_B,_D,_F
SYM_A_B=
LEFT:dpair=>SIG_A_P
RIGHT:bubble=>SIG_B
;
`END_REPLICATE
SYM_A_B=
LEFT:dpair=>SIG_A_P
RIGHT:bubble=>SIG_B
;
SYM_C_D=
LEFT:dpair=>SIG_C_P
RIGHT:bubble=>SIG_D
;
SYM_E_F
LEFT:dpair=>SIG_E_P
RIGHT:bubble=>SIG_F
;

Here we have multiple SUB_DEFS with 3 items each in the replace_list
_A=>_A,_C,_E   
and 
_B=>_B,_D,_F 

so the `REPLICATE loop will attempt to perform 2 substitutions on 
each line surrounded by the `REPLICATE

any occurrence of _A will be replaced by _A, then _C and  _E
any occurrence of _B will be replaced by _B then _D and _F

Notice that the on the Lines containing SYM_A_B=, both substitutions 
are performed,
resulting in: SYM_A_B=, SYM_C_D= and SYM_E_F=



ITERATVE_LOOPS:

These loops are  very similar to iterative loops most commonly encountered in programming languages like C and Perl. 

With the advent of the other loop structures, this one has become less used for building symbols, but can still be used to perform some

simple substitutions to avoid having to input many similar lines of MATCH_STATEMENTS


`FOR (LOOP_VAR=START_VAL; LOOP_VAR COMP_OPERATOR END_VAL; LOOP_VAR INC_DEC_OPERATOR)


SDL_STATEMENTS (some with `VARNAME:: for substitution )

`END_FOR (or `ENDFOR) 

The LOOP_VAR name follows the same rules as the other LOOP_TYPES, It must to start with a letter from A-Z. There is no restriction on how long the LOOP_VAR name is.

The COMP_OPERATOR can be one of  ==, >, <, >=, or <=

The INC_DEC_OPERATOR can be ++, --, +=n, -=n

ITERATIVE_LOOP_EXAMPLE:

Iterative LoopExpandedNotes

DIFF_PAIR_SYMBOL=
`FOR (i=0;i<=3;i++)
right=>dp_o_sig_p`i::
right=>dp_o_sig_n`i::
left=>dp_i_sig_p`i::
left=>dp_i_sig_n`i::
!BSS+1
`END_FOR
;

DIFF_PAIR_SYMBOL=
right=>dp_o_sig_p0
right=>dp_o_sig_n0
left=>dp_i_sig_p0
left=>dp_i_sig_n0
!BSS+1
right=>dp_o_sig_p1
right=>dp_o_sig_n1
left=>dp_i_sig_p1
left=>dp_i_sig_n1
!BSS+1
right=>dp_o_sig_p2
right=>dp_o_sig_n2
left=>dp_i_sig_p2
left=>dp_i_sig_n2
!BSS+1
right=>dp_o_sig_p3
right=>dp_o_sig_n3
left=>dp_i_sig_p3
left=>dp_i_sig_n3
!BSS+1
;

Before PartBuilder added the dpair Modifier to the PIN_MATCH_STATEMENT Iterative loops were very useful to add a bus of diff pairs to a symbol.

The equivalent of this loop using a dpair modifier in the SDL (without any need for the loop) would be

DIFF_PAIR_SYMBOL=
right:dpair=>dp_o_sig[0:3]_p
left:dpair=>dp_i_sig[0:3]_p

;

The iterative loop still provides value if a part had some oddly named diff_pairs where PartBuilder cannot automatically detect the mates using its exhaustive diff_pair detection algorithm.

DDR_DQ_ITER=
`FOR (i=0;i<=3;i++)
`LET j=`i::*8+7
`LET k=`i::*8
right=>DQ[`j:::`k::]
right=>spacer
right=>DQS_P`i::
right=>DQS_N`i::
right=>spacer
`END_FOR
;

DDR_DQ_ITER=
right=>DQ[7:0]
right=>spacer
right=>DQS_P0
right=>DQS_N0
right=>spacer
right=>DQ[15:8]
right=>spacer
right=>DQS_P1
right=>DQS_N1
right=>spacer
right=>DQ[23:16]
right=>spacer
right=>DQS_P2
right=>DQS_N2
right=>spacer
right=>DQ[31:24]
right=>spacer
right=>DQS_P3
right=>DQS_N3
right=>spacer
;

This iterative loop along with the variable assignments, performs the same bus slicing operation shown in the REPEAT_LOOP example above.

The cumbersome math was the reason PartBuilder implemented the REPEAT_LOOP to make it easier to break apart busses.