Shop OBEX P1 Docs P2 Docs Learn Events
New Pin Instructions — Parallax Forums

New Pin Instructions

We have two empty "MNEM {#}D,{#}S' instruction slots.

I'm thinking it would be good to make some shortcut/shorthand instructions for select cases of WRPIN, where an 8- or 9-bit immediate #D could get some quick business done.

I've thought of a few possibilities:
EEEE 1011110 1LI DDDDDDDDD SSSSSSSSS    SETPIN  {#}D,{#}S

	{#}D			WRPIN {#}D equivalent
	%000HHHLLL		%0000_0000_000_0000_000HHHLLL_00_00000_0		Logic in, digital out
	%001HHHLLL		%0000_0000_000_0011_000HHHLLL_00_00000_0		Schmitt in, digital out
	%010HHHLLL		%0000_0000_000_0110_000HHHLLL_00_00000_0		A>B in, digital out
	%0110DDDDD		%0000_0000_000_1100_0DDDDDDDD_00_00000_0		A>Level in, 1.5k out
	%01110xxxx		%0000_0000_000_0100_000101101_00_00000_0		Schmitt latch with 100uA hold
	%01111xxxx		%0000_0000_000_0111_001001001_00_00000_0		A follows B, 1.5k out
	%1HHHHLLLL		%0000_0000_000_101x0_HHHHLLLL_10_00000_0		Bit DAC output

EEEE 1011111 0LI DDDDDDDDD SSSSSSSSS    SETDAC  {#}D,{#}S

	{#}D			WRPIN {#}D equivalent
	%DDDDDDDD		%0000_0000_000_101x0_DDDDDDDD_00_00000_0		Set DAC output to D[7:0]

These modes could be lots of different things. Just have to narrow down the most-useful ones.


Here are the raw pin modes:

Pin_Modes.png
«1345

Comments

  • cgraceycgracey Posts: 14,131
    edited 2018-11-08 16:12

    This would make it really easy to make a pin into a DAC output:
            SETDAC  dacval,#pin
            DIRH    #pin
    


    Or, say you want to pick drive levels for a pin:
            SETPIN  #%HHHLLL,#pin
    


    This saves the trouble of dealing with unwieldy ##D values or static longs located elsewhere in your code.
  • cgracey wrote: »
    This saves the trouble of dealing with unwieldy ##D values or static longs located elsewhere in your code.

    That's pretty cool.
    Neat little feature.
  • This saves the trouble of dealing with unwieldy ##D values or static longs located elsewhere in your code.

    Yes. Nice optimization
  • Just playing devil's advocate:

    1. How many instructions is this saving?
    2. How often is the this instruction typically going to be executed?
  • Seairth wrote: »
    Just playing devil's advocate:

    1. How many instructions is this saving?
    2. How often is the this instruction typically going to be executed?

    Playing the same role, this would use up both of the two empty D,S slots being kept for a rainy day. Are these two new instructions equally important? Could D[8] of SETDAC allow more modes in a single instruction?

  • Well, SETDAC and GETADC sound like a couple nice macros. I'm sure it would make those capabilities much more accessible.
  • jmgjmg Posts: 15,140
    cgracey wrote: »
    This would make it really easy to make a pin into a DAC output:
            SETDAC  dacval,#pin
            DIRH    #pin
    


    Or, say you want to pick drive levels for a pin:
            SETPIN  #%HHHLLL,#pin
    


    This saves the trouble of dealing with unwieldy ##D values or static longs located elsewhere in your code.

    So you are making a teensy ROM here that packs some common expansions into silicon ?
    I can certainly see the appeal in reducing COG memory impact, but are there other ways to reduce such impact ?

    eg

    Can an init-table be placed in HUB, (or LUT) for example, and then a simple loop does all port inits ?
    Or is is simpler to just have any such do-once init code in HUB, whilst the main code runs in COG.

    Those are all tool-flow solutions, that do not need to consume any verilog silicon, and they are more flexible.
  • Cluso99Cluso99 Posts: 18,066
    edited 2018-11-08 19:21
    I am concerned about the feature creep.

    How is this going to impact the current silicon testing?

    Having said this, I am finding a problem converting P1 code. The big "gotcha" is the missing equivalent JMPRET [D],[#]S instruction. The problem comes with self-modifying code, and the direct 9-bit jumps. There were other tricks done with the WC and WZ setting but these are rarer and can be overcome with additional instructions.

    It would make a huge difference having a JMPRET instruction equivalent. What does it need?
    It needs a bit for the NR case where the instruction is just a JMP or RET where no return address is being written. A bit for immediate #S (an "I" bit) is required. S is either an immediate goto address in cog, or a cog register storing a 9-bit cog address in its S bits.
    For the JMPRET or CALL equivalent, the cog return address is written to the S bits in the cog register pointed to in the 9-bit D address.
    Note this instruction only works in COG. Addresses are direct, not relative, because they can also be set using SETS and SETD (formerly MOVS and MOVD) instructions in self-modifying code.

    Now, we can use the JMP {#}S direct 20-bit address instruction for the JMP and RET replacement. The SETS instruction works here too. The upper address bits just remain as 0's. BUT, there is the case where we use JMP S to jump to an address stored in the S-bits of the cog register pointed to by the S address. That S-register might have higher bits set (ie not 0's) and in that case, the JMP S direct 20-bit instruction doesn't work.

    Next are the JMPRET and CALL equivalents which are really just a JMPRET D,{#}S where D is a 9-bit cog register where the 9-bit cog return address will be written into its S field, and {#}S is the 9-bit cog address to jump to. Optional WC and WZ bits would be nice but as I said can be worked around. They would be set as per P1, not P2. A shortcut can be use to just set Z and C. In P1, the WZ was used to set NZ as the only way Z could be set is if the code wrapped to cog $000 from the shadow register $1FF which is almost impossible. I cannot recall if WC was ever used and how it was set.

    Thoughts please.
  • You can use the CALLD in place of JMPRET. That's what I do for p2gcc. There are two forms for CALLD. One is CALLD D, {#}S. This allows for the target address to be in cog RAM, or it can be an immediate value between 0 and 511. The other form is CALLD PA/PB/PTRA/PTRB,#A, where A is an 20-bit immediate value.

    p2gcc always uses CALLD PA,#A. If you are running from cog memory you can use the first form. If the target address is in hub RAM, the return address will need to be in PA, PB, PTRA or PTRB.
  • cgraceycgracey Posts: 14,131
    At first, I thought of making an instruction that would just write bits 15..8, like a limited WRPIN, leaving the rest of the bits as they were. That would make it easier to modify a DAC value, or drive levels, but would still required initial setup with a full WRPIN. I'm just thinking it would be nice to simplify some common use cases. Makes it especially easy for the beginner.
  • It seems to me that simplifying common use cases should perhaps be done in the tools and libraries rather than the silicon. Changes to the instruction set at this point in the development cycle seems *really* risky.
  • cgraceycgracey Posts: 14,131
    This is pretty low risk. So is making PTRA auto-increment on RD/WRLUT.
  • ElectrodudeElectrodude Posts: 1,614
    edited 2018-11-08 21:56
    But these new instructions use up the last two D/S slots to do something that can already be done with WRPIN. Can't they just be assembler macros at the expense of a few longs?
  • But these new instructions use up the last two D/S slots to do something that can already be done with WRPIN. Can't they just be assembler macros at the expense of a few longs?
    I second that motion!

  • I agree with the others, why make an instruction when it could just be done with existing instructions and some better documentation?
    I don't like the idea of it being a macro expansion if it results in more than one instruction result, that obfuscates things.

    Good documentation with examples showing exact instructions needed for these common cases will go a long way.
  • Pushback on feature additions. That's new.
  • Roy Eltham wrote: »
    I agree with the others, why make an instruction when it could just be done with existing instructions and some better documentation?
    I don't like the idea of it being a macro expansion if it results in more than one instruction result, that obfuscates things.

    Good documentation with examples showing exact instructions needed for these common cases will go a long way.

    setdac(dacval) and setpin(%HHHLLL) could be compiler pseudo-functions that return a long suitable for passing to wrpin. You could then do any of the following:
          wrpin  cfg, #pin
    cfg   long   setpin(%HHHLLL)
    
          wrpin  ##setpin(%HHHLLL), #pin
    
    PUB set_pin(pin)
      wrpin(setpin(%HHHLLL), pin)
    
  • Electrodude,
    That looks like a good solution to me. No need for new instructions at all!
  • cgraceycgracey Posts: 14,131
    Roy Eltham wrote: »
    Electrodude,
    That looks like a good solution to me. No need for new instructions at all!

    The only point is to make single-instruction common-use cases available for the assembly language programmer. Throwing in ## or dedicated static longs in registers is kind of messy if it can be cleaned up a bit.
  • but at the cost of using up instruction space and making yet another change to the verilog/hardware....
    This is something that can and should be done in software/documentation.

    You could use your argument to add 100s of new instructions all over the place. I really wish you wouldn't.
  • cgraceycgracey Posts: 14,131
    Roy Eltham wrote: »
    but at the cost of using up instruction space and making yet another change to the verilog/hardware....
    This is something that can and should be done in software/documentation.

    You could use your argument to add 100s of new instructions all over the place. I really wish you wouldn't.

    We've got DACs that can be instantly used on every pin. It's a bit of a shame to not make it as easy as possible. Same with the pin-drive modes.

    WRPIN, with that 32-bit word with all those bitfields, will stop everyone in their tracks. Nice to be able to get resistor or current drive with 'PINDRV #%HHHLLL,pin' or revert to normal with 'PINDRV #0,pin'. And to make a DAC output with 'PINDAC #value,pin' is so simple, too.

    This would eat the empty instruction slots, but take few gates. I see it as a nice creature comfort for the programmer.
  • But we can do it all in the compiler/assembler without changing anything in the verilog/hardware.
    You are over worried about minor syntax differences I think.
  • cgraceycgracey Posts: 14,131
    edited 2018-11-09 08:43
    Roy Eltham wrote: »
    But we can do it all in the compiler/assembler without changing anything in the verilog/hardware.
    You are over worried about minor syntax differences I think.

    These instructions save longs (## or register) and are way easier to remember.

    For example, this (2 longs):
    WRPIN ##%0000_000HHHLLL_00_00000_0,pin
    

    becomes (1 long):
    PINDRV #%HHHLLL,pin
    


    For DAC setting, the savings is even greater.

    This (3 longs):
            SETBYTE dacmode,dacvalue,#1
            WRPIN   dacmode,pin
    ...
    dacmode LONG    %10110_00000000_00_00000_0
    

    becomes (1 long):
            PINDAC  dacvalue,pin
    
  • I dunno if saving a long is worth it, and I disagree that it's "way" easier. I admit it's easier, but not that much.
  • jmgjmg Posts: 15,140
    cgracey wrote: »
    Roy Eltham wrote: »
    but at the cost of using up instruction space and making yet another change to the verilog/hardware....
    This is something that can and should be done in software/documentation.

    You could use your argument to add 100s of new instructions all over the place. I really wish you wouldn't.

    We've got DACs that can be instantly used on every pin. It's a bit of a shame to not make it as easy as possible. Same with the pin-drive modes.

    WRPIN, with that 32-bit word with all those bitfields, will stop everyone in their tracks. Nice to be able to get resistor or current drive with 'PINDRV #%HHHLLL,pin' or revert to normal with 'PINDRV #0,pin'. And to make a DAC output with 'PINDAC #value,pin' is so simple, too.

    This would eat the empty instruction slots, but take few gates. I see it as a nice creature comfort for the programmer.

    If all you seek here is "nice creature comfort for the programmer" you can do that with some macros, and maybe some Config wizards, all external to P2 silicon.
    The better vendor tools have graphical config tools, that show the result of the code, as in operating mode, critical init values etc,
    Some even have simulators that do the same thing, but live at code-step, so you can watch the before.after config and see the modes change.


    The only silicon reason I could see for this would be to save bytes of code space, but that does not seem to be a focus here ?

  • I've been using P2 silicon for a few weeks now and here's what I have experienced.
    I have found it a bit clunky having to fiddle with building a config long and then going throught the smart pin write as an additional step.

    This scheme seems way more logical to deal with the DAC direct and remove the "moddle" man.
    My 2 cents...
  • Cluso99Cluso99 Posts: 18,066
    Initially I found dealing with those 32-bit configuration values cumbersome. I was using the HUBSET instruction to set the clocking.
    So, I worked out the constant sections first, then applied them to combine into a new constant. Turned out to be quite simple really.
    See my sample clock setting code in the P2 Tricks and Traps thread.

    Nothing special required here. No changes to the silicon or the compiler.

    IMHO, setting the pin values for pull-ups/pulldowns will typically only be done once. Seems a shame to waste silicon for this.
  • ElectrodudeElectrodude Posts: 1,614
    edited 2018-11-09 18:25
    cgracey wrote: »
    Roy Eltham wrote: »
    But we can do it all in the compiler/assembler without changing anything in the verilog/hardware.
    You are over worried about minor syntax differences I think.

    These instructions save longs (## or register) and are way easier to remember.

    For example, this (2 longs):
    WRPIN ##%0000_000HHHLLL_00_00000_0,pin
    

    becomes (1 long):
    PINDRV #%HHHLLL,pin
    


    For DAC setting, the savings is even greater.

    This (3 longs):
            SETBYTE dacmode,dacvalue,#1
            WRPIN   dacmode,pin
    ...
    dacmode LONG    %10110_00000000_00_00000_0
    

    becomes (1 long):
            PINDAC  dacvalue,pin
    

    For your first example,
    WRPIN ##%0000_000HHHLLL_00_00000_0,pin
    
    could become
    WRPIN ##pindrv(%HHHLLL), pin
    

    For your second case, if you made setq before wrpin replace byte 1 of the wrpin with its own argument:
            SETBYTE dacmode,dacvalue,#1
            WRPIN   dacmode,pin
    ...
    dacmode LONG    %10110_00000000_00_00000_0
    
    could become
            SETQ    dacvalue
            WRPIN   ##pindac(0-0),pin ' 0-0 replaced with setq value
    

    You could also use setq with pindrv(0-0):
            SETQ    #%HHHLLL
            WRPIN   ##pindrv(0-0), pin
    

    EDIT: setpin -> pindrv
  • TonyB_TonyB_ Posts: 2,099
    edited 2018-11-09 18:31
    I think there should be a rule that any new instruction must replace at least three existing longs. SETPIN appears to be a waste of a valuable D,S slot, frankly. There's a lot of testing still to be done and it could be needed.

    In contrast, SETDAC/PINDAC looks to be useful and could be more so. I'd call it WRDAC and here are a couple of options for enhanced capability:

    Option 1 - single pin
      S[8:0]	Action
    000PPPPPP	DAC[S[5:0]]=D[7:0]
    001PPPPPP	DAC[S[5:0]]=D[8:15]
    010PPPPPP	DAC[S[5:0]]=D[16:23]
    011PPPPPP	DAC[S[5:0]]=D[24:31]
    100PPPPPP	DAC[S[5:0]]=!D[7:0]
    101PPPPPP	DAC[S[5:0]]=!D[8:15]
    110PPPPPP	DAC[S[5:0]]=!D[16:23]
    111PPPPPP	DAC[S[5:0]]=!D[24:31]
    

    Option 2 - single/multiple pins
      S[8:0]	Action
    000PPPPPP	DAC[S[5:0]]=D[7:0]
    001PPPPPP	DAC[S[5:0]]=D[8:15]
    010PPPPPP	DAC[S[5:0]]=D[16:23]
    011PPPPPP	DAC[S[5:0]]=D[24:31]
    100PPPPPx	DAC[S[5:1]_0]=D[7:0],   DAC[S[5:1]_1]=D[8:15]
    101PPPPPx	DAC[S[5:1]_0]=D[16:23], DAC[S[5:1]_1]=D[24:31]
    110PPPPxx	DAC[S[5:2]_00]=D[7:0],   DAC[S[5:2]_01]=D[8:15],
    		DAC[S[5:2]_10]=D[16:23], DAC[S[5:2]_11]=D[24:31]
    111PPPPPP	DAC[S[5:0]]=!D[7:0]
    
  • Option 1 above is a sort of GETBYTE for DACs, with optional inversion for AC outputs. Data could be packed into a long to save space with no time penalty.
Sign In or Register to comment.