Shop Learn
PropBASIC update June 2017 — Parallax Forums

PropBASIC update June 2017

I know it has been awhile, but I've been quite busy.
But now I am working on updating PropBASIC with a couple problems I've found.

So I am asking if anyone that uses PropBASIC has found any issues or problems that they can let me know about, I will see about getting them fixed.

Also, if there is a feature that you would like to see, let me know now while I have my head in the code.

One of the major changes is that the "*", "**", "*/", "/", and "//" operators now use subroutines instead of always being in-line code. That should save quite a bit of code space.

Anyway, please let me know ASAP so I can post this new version quickly. (Next week or so ???).

Bean

Comments

  • Thank you Bean, a very generous offer.

    Hopefully some of the regular PropBASIC lovers will chime in. I'll also refer back to my code and give this some serious thought in the next 24 hours.

    In the meantime, I'm posting a reminder to the PropBASIC thread, so all members subscribed will get notified.




  • BeanBean Posts: 8,122
    Here are the fixes/changes I have implemented so far since version 00.01.42
    Version 00.01.43
      Changed SERIN so it doesn't wait for stop bit
      Optimize Divide and remainder operation [June 27,2014]
    
    Version 00.01.44
      Made PAUSE a subroutine
      Made *, */, **, /, and // operator subroutine
      Allow FREQ with RCSLOW or RCFAST "ProcessFreq" Feb 3, 2016
    
    Version 00.01.45
      Changed A = x - A to generate
        neg A,A
        adds A,x
      Fixed two range errors
      Fixed __remainder was __temp1, but should be __temp4 now Mar 7, 2016
      Fixed: I2C commands in LMM mode require minimum of 150 WAITCNT  Jun 28,2017
      Fixed: I2CSTART locks up if no pull-ups (should only try 10 times) Jun 29,2017
    

    Bean
  • Good to hear from you Terry...I raced to this post like a kid at Xmas....
  • Mickster wrote: »
    Good to hear from you Terry...I raced to this post like a kid at Xmas....

    Dammit I typed a lot more but it vanished..will have to wait because I have to be somewhere.

  • jmgjmg Posts: 14,946
    Bean wrote: »
    ...
    Also, if there is a feature that you would like to see, let me know now while I have my head in the code.
    Nice to see an update.
    I'll make a more ambitious suggestion - add a P2 code generation switch ? :)


  • RaymanRayman Posts: 12,647
    How does one get a PBASIC program onto Prop chip these days?
  • jmgjmg Posts: 14,946
    Rayman wrote: »
    How does one get a PBASIC program onto Prop chip these days?


    There is this
    http://forums.parallax.com/discussion/149659/propbasic-how-to-use-propbasic-in-simpleide
    and some command line choices are here
    http://forums.parallax.com/discussion/162749/solved-prop-1-loader-for-linux
  • Rayman wrote: »
    How does one get a PBASIC program onto Prop chip these days?
    PropellerIDE (like SimpleIDE, but for Spin) also supports PropBASIC.

  • BeanBean Posts: 8,122
    Also any suggestions for the documentation are welcomed too.

    Bean
  • jmgjmg Posts: 14,946
    edited 2017-06-30 10:28
    Bean wrote: »
    Also any suggestions for the documentation are welcomed too.

    More examples, and better reports are always good.

    a) eg you could add the Reciprocal Frequency Counter (& others) to the examples dir, as well as the examples given in the PDF.

    The examples I like the best are built-dir copies, so they include all report and list files, and downloadable files.
    This allows users to verify against a known-good copy, and even download the example, before even starting the compiler.

    That usually means something like this :
    \examples\master\Blink\
    \examples\user\Blink\
    \examples\master\Ping\
    \examples\user\Ping\
    \examples\master\FreqCtr\
    \examples\user\FreqCtr\

    \master\ has the original build copies and user starts as a clone of that, but users are expected to modify/rename in these directories.


    b) For better reports, I see it runs like this :
    PropBasic Version 00.01.44 Jul 13, 2014
    Finished Compile. 116 Lines Read, 228 Lines Generated,  0 Warnings, 0 Errors.
    
    Build version is good, as are lines read, and Lines generated is OK, but I'd rather know how much of the CHIP was used.

    So a report of how much COG is consumed by CODE and VARs and how much HUB is used is more useful.

    This info should appear on the screen, and also in the output file as a comment line.


    c) Numeric values always help.
    Example - DOCs have
    I2SPEED Sets the clock speed for I2C operations.
    I2CSPEED multipier
    * “multiplier” may be a floating point value
    A value of 2 would make I2C operations twice as fast as normal.
    A value of 0.5 would make I2C operations half as fast as normal


    but nowhere can I find the i2c clock speed in kHz. With parts supporting 100k, 400k, 1M, 3.4M info on actual i2c CLK is important.

    Likewise, SERIN, SEROUT, SHIFTIN, SHIFTOUT all mention Baud or Speed, but give no limits on what valid range is supported ?

  • jmg wrote: »
    Bean wrote: »
    Also any suggestions for the documentation are welcomed too.

    More examples, and better reports are always good.

    a) eg you could add the Reciprocal Frequency Counter (& others) to the examples dir, as well as the examples given in the PDF.

    The examples I like the best are built-dir copies, so they include all report and list files, and downloadable files.
    This allows users to verify against a known-good copy, and even download the example, before even starting the compiler.

    That usually means something like this :
    \examples\master\Blink\
    \examples\user\Blink\
    \examples\master\Ping\
    \examples\user\Ping\
    \examples\master\FreqCtr\
    \examples\user\FreqCtr\

    \master\ has the original build copies and user starts as a clone of that, but users are expected to modify/rename in these directories.


    b) For better reports, I see it runs like this :
    PropBasic Version 00.01.44 Jul 13, 2014
    Finished Compile. 116 Lines Read, 228 Lines Generated,  0 Warnings, 0 Errors.
    
    Build version is good, as are lines read, and Lines generated is OK, but I'd rather know how much of the CHIP was used.

    So a report of how much COG is consumed by CODE and VARs and how much HUB is used is more useful.

    This info should appear on the screen, and also in the output file as a comment line.


    c) Numeric values always help.
    Example - DOCs have
    I2SPEED Sets the clock speed for I2C operations.
    I2CSPEED multipier
    * “multiplier” may be a floating point value
    A value of 2 would make I2C operations twice as fast as normal.
    A value of 0.5 would make I2C operations half as fast as normal


    but nowhere can I find the i2c clock speed in kHz. With parts supporting 100k, 400k, 1M, 3.4M info on actual i2c CLK is important.

    Likewise, SERIN, SEROUT, SHIFTIN, SHIFTOUT all mention Baud or Speed, but give no limits on what valid range is supported ?

    +1
    IIRC we are also lacking SPI examples.

  • jmgjmg Posts: 14,946
    edited 2017-07-03 20:55
    Mickster wrote: »
    +1
    IIRC we are also lacking SPI examples.
    Good point, that's also topical as I have a high speed SPI slave candidate here on the bench.

    Captured Waveforms below, tuned for speed. CLK comes from IO, but it can have a ping-style trigger. As shown, receives 64b, then sends 64b.
    Example would be 40 pins of IO expansion, and (spare) 24 bits of additional smarts each way.

    5.5296MHz Ref clk can come from slave to sync. (example is 8MHz SCK as that's the variant I have )

    I think the Prop code needed for Prop side of this is ~ 17 lines PASM - how much spare space is there in PropBASIC SPI area ?

    Addit: I found this great thread
    http://forums.parallax.com/discussion/155143/waitpxx-detailed-timing
    which indicates Pin-Pin delays of 6~7 SysCLKS + 9.31ns Tco
    That means whilst the software can service to a SPI Slave of SysCLK/16, the hardware just does not quite manage a timing budget... :(
    If add WAIT to every clock edge, that does drops the clock peak, but is more tolerant, and does shrink the code & saves a wire.
    Seems the 5MHz+ link speed needs to drop to something like < 80M/22 = 3.636MHz & 96M/22 = 4.363MHz MAX values.
    That covers common values of 2.7648MHz & 3MHz & 4MHz


  • VonSzarvasVonSzarvas Posts: 2,712
    edited 2017-06-30 12:50
    The one thing I can offer is to do with compile-time renaming of local variables.

    The aim is to enable the use of local vars in subs/funcs that can have the same name in other subs/funcs. Particularly handy when using libraries.

    example:
    RX_WAIT            FUNC    1,3
    TX_STR             SUB     1,2
    
    
    FUNC RX_WAIT 
    
            idx         VAR     LONG
            val         VAR     LONG 
    
            val = idx + 12
    
    
    .....etc...
    
    
    SUB TX_STR  
    
            idx         VAR     LONG
            val         VAR     LONG
    
            val = idx + 12
    
    .....etc...
    
    
    



    The PropBASIC compiler might do a pre-pass search of the code files (cogs, libs, etc..), and for each SUB or FUNC defined, then it might rename the variables in the actual SUB code to have a prefix with the SUB name. Thus solving any conflicts before the compiler does it's usual compiling tasks!

    This pre-pass could be as simple as a textual search/replace. (Perhaps! I'm no Delphi expert!)

    End result of this example (after this pre-pass) would be:
    RX_WAIT            FUNC    1,3
    TX_STR             SUB     1,2
    
    
    FUNC RX_WAIT 
    
            FUNC_RX_WAIT_idx         VAR     LONG
            FUNC_RX_WAIT_val         VAR     LONG 
    
            FUNC_RX_WAIT_val = FUNC_RX_WAIT_idx + 12
    
    .....etc...
    
    
    SUB TX_STR  
    
            SUB_TX_STR_idx         VAR     LONG
            SUB_TX_STR_val         VAR     LONG
    
            SUB_TX_STR_val = SUB_TX_STR_idx + 12
    
    .....etc...
    
    
    


    With the local variable names made unique, the compiler should be able to compile the code as the user intended, without any conflicts or duplicate local var name errors.


    This sort of naming convention is something I've used for a while to ensure zero conflicts, but it would still be handy (and certainly time saving) to have the compiler handle this automatically. I think the users original source code would be cleaner without having to manually add all the prefixes too, which makes sharing code easier!

    One other thought- you'd probably need to process the LOAD-ed code libs in a similar way... Perhaps using the library filename as part of the variable name prefix.

    example:
    LOAD "delays_lib.pbas"
    
    DELAY_MS        SUB     1
    DELAY_US        SUB     1
    
    SUB DELAY_MS
    
      delay VAR LONG
      delay = __param1 * 1000
      PAUSE delay
    
      ENDSUB
    
    SUB DELAY_US
    
      delay VAR LONG
      delay = __param1
      PAUSE delay
    
      ENDSUB
    


    becomes:
    DELAY_MS        SUB     1
    DELAY_US        SUB     1
    
    SUB DELAY_MS
    
      delays_lib_SUB_DELAY_MS_delay VAR LONG
      delays_lib_SUB_DELAY_MS_delay = __param1 * 1000
      PAUSE delays_lib_SUB_DELAY_MS_delay
    
      ENDSUB
    
    SUB DELAY_US
    
      delays_lib_SUB_DELAY_US_delay VAR LONG
      delays_lib_SUB_DELAY_US_delay = __param1
      PAUSE delays_lib_SUB_DELAY_US_delay
    
      ENDSUB
    


    Something like that.

  • jmgjmg Posts: 14,946
    edited 2017-06-30 21:22
    VonSzarvas wrote: »
    The one thing I can offer is to do with compile-time renaming of local variables.
    ...
    Something like that.
    Sounds a good idea, but the _SUB_ & _FUNC_ substrings in the examples do not seem to be needed ?

    I did a quick check, and PropBASIC errors on the same name for SUB and FUNC anyway, (even if someone thought using the same name was a good idea.)



  • jmgjmg Posts: 14,946
    Bean wrote: »
    Also, if there is a feature that you would like to see, let me know now while I have my head in the code.
    Another idea... Refreshing with the manual, I see there is support for DJNZ, which is nice.

    Missing for tightest code loops however, seem to be support for Carry Flag.

    I'd suggest allowing

    Pin = CY
    CY = Pin

    Plus Variant of SHR, SHL, that include C, and can manage 32 or 64b, or > 64b

    Current SHR syntax is value1 = value2 SHR value3

    but that makes it harder to support 64b - so Maybe new set of

    ROR,RORC,ROL,ROLC can work with a param list.

    RORC value1 'update CY and does 32b, with CY

    RORC Value1,Value2 'update CY and does 64b with CY

    Then serial code is something like this, which I think maps to 4 lines of PASM ?
    SerLoop:
    RORC Value1,Value2
    TxPin = CY
    DJNZ BitCtr,SerLoop



  • Let me add another voice to the chorus of "thank you" for the generous effort, Bean. It has been a few months since I have written any code, but this thread motivated me to download the PropellerIDE and play around a bit.

    As mentioned, it would be good to add a definitive voice in the documentation regarding questions that have come up in the forum:

    I2CSPEED ... what is "normal"? How does "normal" vary with clock settings? I recall seeing this in the forums, but did not take notes.

    SHIFTIN(OUT) ... what does "speed" do and does it vary with clock settings? My notes are that it defaults to 50kHz and the practical limit is speed=20 for 1MHz. Exceeding the speed limit causes code to hang until cnt wraps around.
    ... valid range for "bits". My notes are that up to 32 bits can be written.

    Those are examples of things that have tripped me up and taken time to work around, so it may be helpful to another new user.

    Cheers!
  • tritoniumtritonium Posts: 522
    edited 2017-07-03 15:53
    Hi
    I'm REALY HAPPY with propbasic- but sometimes an error is flagged- no by basic but by brads spin tool.
    For example
    do
    	qclk=optclk			'get clock polarity
    	if qclk <> 0 then
    		same=sameval	'not zero reload counter
    	else
    		dec same
    	'endif
      loop until same=0
    

    As you can see I commented out the endif and got this error

    baserr.PNG
    baserr.PNG

    Propbasic says 0 warnings 0 errors but bst flags it up as an unresolved symbol.

    Its no big deal- it figure it out eventually but thought I would mention it.
    It would also be nice to know how many bytes are left for each cog used.

    Nevertheless its great the way it is.

    Dave
  • jmg wrote: »

    Plus Variant of SHR, SHL, that include C, and can manage 32 or 64b, or > 64b

    +1

  • jmgjmg Posts: 14,946
    Bean wrote: »
    Also, if there is a feature that you would like to see, let me know now while I have my head in the code.

    .. another idea..
    I see there is NOP, which is great..
    I suggest making that slightly smarter with

    NOP CountConst

    So one line can insert 1(default),2,3,etc NOPs, and this should generate WAITCNT(or DJNZ?) when Count is large enough that WAITCNT is smaller than many NOP.
    PAUSE operators are time based, this one is Cycles based.
  • pmrobertpmrobert Posts: 660
    edited 2017-07-26 17:24
    Perhaps an fourth additional parameter for serin/serout optionally specifying byte size? I understand that a 32 bit serial byte would be rather nonstandard but, in the case where longs are being transferred it does save a few shr and shl conversions at both ends of the connection and should be marginally faster for a couple of reasons.

    Edit: A couple of minutes mucking around in the serout code generator required 3 code changes as remarked in attached code.
    Unit _SERIAL;
    
    {$MODE Delphi}
    
    Interface
    
    // *************************************** Variable byte length mod
    // Uses GLOBAL;
    Uses GLOBAL, SysUtils;
    // ***************************************
    
    Procedure ProcessSerOut;
    Procedure ProcessSerIn;
    
    Implementation
    // ***************************************  Variable byte length mod
    // Procedure ProcessSerOut; { SerOut Pin,Baud,Data } <- Original Code, just a comment
    Procedure ProcessSerOut; { SerOut Pin,Baud,Data,ByteSize } // Comment change
    // ***************************************
    { OT = Open drain, driven high }
    { ON = Open source, driven low }
    Var
      poDelayVar: PVarObj;
      lBaudDelay: LongInt;
      lBaudRate: Longint;
      sBaudRate, sBaudDelay: String[15];
      sMode: Char;
      bOpenMode: Boolean;
      sNextCharLabel, sZeroCharLabel: String;
      iValError: Integer;
    Begin
      sNextCharLabel:='';
      If g_bInternalClock Then Warning(c_iWarningInternalClock, 1);
      // ***************************************  Variable byte length mod
      // If g_iCmdCnt = 6 Then <-Original Code 
      If g_iCmdCnt = 8 Then // 8 accomodates additional comma and value for byte length param
      // ***************************************
      Begin
        If g_asCmdLine[3] <> ',' Then Error(c_iErrorCommaExpected, 3);
        If g_asCmdLine[5] <> ',' Then Error(c_iErrorCommaExpected, 5);
        If g_asCmdLine[7] <> ',' Then Error(c_iErrorCommaExpected, 7); // 4th parm comma check
        // temp1 = pin mask
        Case g_apoCmdVars[2]^.eGetType of
          e_Pin:
            Begin
              OutStr('  mov __temp1,'+g_apoCmdVars[2]^.sGetName);
            End;
          e_ShortConst:
            Begin
              OutStr('  mov __temp1,#1');
              OutStr('  shl __temp1,#'+g_apoCmdVars[2]^.sGetName);
            End;
          e_LongConst, e_LongVar:
            Begin
              OutStr('  mov __temp1,#1');
              If g_apoCmdVars[2]^.m_bVarIndex Then
              Begin
                If g_bLMM Then
                Begin
                  OutStr('  mov __INDEX,#'+g_apoCmdVars[2]^.sGetName);
                  OutStr('  add __INDEX,'+g_apoCmdVars[2]^.m_sIndex);
                  OutStr('  jmp #_LMM_MOVS');
                End
                Else
                Begin
                  OutStr('  add '+g_apoCmdVars[2]^.m_sIndex+',#'+g_apoCmdVars[2]^.sGetName);
                  OutStr('  movs $+2,'+g_apoCmdVars[2]^.m_sIndex);
                  OutStr('  sub '+g_apoCmdVars[2]^.m_sIndex+',#'+g_apoCmdVars[2]^.sGetName);
                End;
                OutStr('  shl __temp1,0-0');
              End
              Else
              Begin
                OutStr('  shl __temp1,'+g_apoCmdVars[2]^.sGetIndexName);
              End;
            End;
          Else Error(c_iErrorInvalidParameter, 2);
        End; // Case
        // temp2 = character
        If g_apoCmdVars[6]^.eGetType =  e_ShortConst Then
        Begin
          OutStr('  mov __temp2,#'+g_apoCmdVars[6]^.sGetName);
        End
        Else If g_apoCmdVars[6]^.eGetType In [e_LongConst, e_LongVar] Then
        Begin
          If g_apoCmdVars[6]^.m_bVarIndex Then
          Begin
            If g_bLMM Then
            Begin
              OutStr('  mov __INDEX,#'+g_apoCmdVars[6]^.sGetName);
              OutStr('  add __INDEX,'+g_apoCmdVars[6]^.m_sIndex);
              OutStr('  jmp #_LMM_MOVS');
            End
            Else
            Begin
              OutStr('  add '+g_apoCmdVars[6]^.m_sIndex+',#'+g_apoCmdVars[6]^.sGetName);
              OutStr('  movs $+2,'+g_apoCmdVars[6]^.m_sIndex);
              OutStr('  sub '+g_apoCmdVars[6]^.m_sIndex+',#'+g_apoCmdVars[6]^.sGetName);
            End;
            OutStr('  mov __temp2,0-0');
          End
          Else OutStr('  mov __temp2,'+g_apoCmdVars[6]^.sGetIndexName);
        End
        Else If g_apoCmdVars[6]^.eGetType In [e_HubByte, e_DataLabel, e_ByteData]  Then
        Begin
          ProcessHubAddrLit(6, '__temp5', 1);
          sNextCharLabel:=NewLabelStr;
          OutStr(sNextCharLabel);
          sZeroCharLabel:=NewLabelStr;
          OutStr('  rdbyte __temp2,__temp5 WZ');
          OutStr('  adds __temp5,#1');
          If g_bLMM Then OutStr('  IF_Z add __PC,#(('+sZeroCharLabel+'-$)*4)-4')
          Else OutStr('  IF_Z  jmp #'+sZeroCharLabel);
        End
        Else Error(c_iErrorInvalidParameter, 6);
        OutStr('  or __temp2,#256'); // Stop bit is 1
        OutStr('  shl __temp2,#1'); // Make room for start bit
    
        sBaudRate:=Upper(g_asCmdLine[4]);
        sMode:=UpCase(sBaudRate[1]);
        If sBaudRate <> '' Then Delete(sBaudRate, 1, 1);
        bOpenMode:=False;
        If sMode = 'O' Then
        Begin
          bOpenMode:=True;
          sMode:=UpCase(sBaudRate[1]);
          If sBaudRate <> '' Then Delete(sBaudRate, 1, 1);
        End;
        Val(sBaudRate, lBaudRate, iValError);
    
        Inc(iValError); // avoid lazarus warning
    
        lBaudDelay:=1000;
        If lBaudRate < 1 Then Error(c_iErrorInvalidParameter, 4)
        Else
        Begin
          lBaudDelay:=g_lFreq Div lBaudRate;
          If lBaudDelay < 10 Then Error(c_iErrorBaudRateTooHigh, 4);
        End;
    
        sBaudDelay:='_'+sBaudRate+'baud';
        poDelayVar:=g_oDevice.pGetVarPtr(sBaudDelay);
        If poDelayVar = Nil Then poDelayVar:=g_oDevice.pAddLongConst(sBaudDelay, lBaudDelay, IntStr(lBaudDelay));
    
        // ************************************ Variable byte length mod
        // OutStr('  mov __temp3,#10'); // Send 10 bits <- Original code
        OutStr('  mov __temp3,#'+(IntToStr(StrToInt(g_apoCmdVars[8]^.sGetName)+2))); // <- New code for variable byte length
        // ************************************
    
        If sMode In ['T', 'N'] Then
        Begin // Normal serial output
          // Start Bit is a "0" bit. High for True, Low for iNverted
          OutStr('  mov __temp4,'+sBaudDelay);
          OutStr('  add __temp4,cnt');
    
          If bOpenMode Then
          Begin // Setup dira and outa
            OutStr('  andn dira,__temp1'); // Make pin an input
            If sMode = 'N' Then OutStr('  or outa,__temp1') // Setup to drive high "ON"
            Else OutStr('  andn outa,__temp1'); // Setup to drive low "OT"
          End
          Else OutStr('  or dira,__temp1'); // Make pin output
    
          OutStr(NewLabelStr);
          OutStr('  shr __temp2,#1 WC');
    
          If bOpenMode = False Then
          Begin
            If sMode = 'T' Then OutStr('  muxc outa,__temp1')
            Else OutStr('  muxnc outa,__temp1');
          End
          Else
          Begin
            If sMode = 'N' Then OutStr('  muxc dira,__temp1')
             Else OutStr('  muxnc dira,__temp1');
          End;
    
          OutStr('  waitcnt __temp4,'+poDelayVar^.sGetName);
          If g_bLMM Then
          Begin
            OutStr('  djnz __temp3,#_LMM_JUMP');
            OutStr('  long @@@'+LabelStr);
          End
          Else OutStr('  djnz __temp3,#'+LabelStr);
          If sNextCharLabel <> '' Then
          Begin
            If g_bLMM Then
            Begin
              OutStr('  sub __PC,#(($-'+sNextCharLabel+')*4)+4');
    //          OutStr('  jmp #_LMM_JUMP');
    //          OutStr('  long '+sNextCharLabel+' * 4');
            End
            Else OutStr('  jmp #'+sNextCharLabel);
            OutStr(sZeroCharLabel);
          End;
          g_bHandled:=True;
        End
        Else Error(c_iErrorInvalidParameter, 4);
      End
      Else Error(c_iErrorInvalidNumberOfParameters, 0);
    End; 
    

    Edit #2: I just noticed that the above code leaves the line idling low. Broke something else I guess! Disregard, looks OK on scope.
  • jmgjmg Posts: 14,946
    pmrobert wrote: »
    Perhaps an fourth additional parameter for serin/serout optionally specifying byte size? I understand that a 32 bit serial byte would be rather nonstandard but, in the case where longs are being transferred it does save a few shr and shl conversions at both ends of the connection and should be marginally faster for a couple of reasons.

    Edit: A couple of minutes mucking around in the serout code generator required 3 code changes as remarked in attached code.
    That's nifty and a good idea.

    Some systems use 9 bits, and/or 2 stop bits, and we have done designs where > 2 stop bits, but exact timing were needed. (IIRC 17 or 19b frames)
    With crystals (eg a Prop at each end) allowing for 32b serial makes very good sense.
    (this will also be compatible with P2 UARTs :) )
Sign In or Register to comment.