Radio Shack LCD: C vs Spin vs +PASM grudge match

2»

Comments

  • David BetzDavid Betz Posts: 14,179
    edited 2014-09-13 - 08:20:33
    jazzed wrote: »
    I fail to understand what grudge match or language war has to do with this topic.
    Seems like the original idea was to prove that Spin was better than C at least for this application. I guess I should stop allowing myself to be drawn into these debates! :-)
  • KMyersKMyers Posts: 433
    edited 2014-09-13 - 09:07:51
    Very interesting thread. Does anyone have a RS part number? Havent been able to find one here in the boonies.

    Thanks!! :lol:
  • cavelambcavelamb Posts: 687
    edited 2014-09-13 - 09:58:53
    KMyers wrote: »
    Very interesting thread. Does anyone have a RS part number? Havent been able to find one here in the boonies.

    Thanks!! :lol:

    2770070 - while they last.
  • KMyersKMyers Posts: 433
    edited 2014-09-13 - 10:12:36
    Thank you cavelamb...
  • cavelambcavelamb Posts: 687
    edited 2014-09-13 - 10:19:09
    David Betz wrote: »
    I guess if you're unwilling to use PASM with C then it will perform worse than Spin+PASM. I'm not sure where the complexity of the sketch came in. Are you talking about the sketch itself plus all of the libraries required to use it? Anyway, it is certainly much easier to use Spin+PASM than it is to use C+PASM. However, the need to use Spin+PASM is mostly due to the fact that Spin often doesn't perform well enough to use on its own.

    Yes, I was referring to the original Arduino Sketch file collection.
    and also the (uncounted) conditional compilation switches.
    It's a lot of megabytes to dig through to find a kilobyte of gold.

    On the other topic, I may be wrong about this, but it seem to me that Spin IS the high level language for the Propeller.
    It's back to that whole Von-Gracey architecture thing.
    Spin is an interpreter-in-ROM, somewhat like the original Apple, TRS-80' etc computers of the olden days, but the
    whole machine was boiled down to a single chip (ok and eeprom, but you get the idea).
    It's a whole lot of power in a very tiny and unique package.
    It's FUN.

    This particular project issue comes from trying to adapt an example from the Arduino world.
    That's where all the good toys are found these days.
    But the example software is so involved and convoluted that I simply can't follow the thread.
    It's easy to lose heart when it's that involved (aka hard) to follow.
    The amount of complication involved - not fun.

    So if you wish,yes, that's a failing on my part, for not embracing C.
  • localrogerlocalroger Posts: 3,416
    edited 2014-09-13 - 11:32:50
    That's nice work SwimDude! Please let us know how it works with the actual hardware.
  • localrogerlocalroger Posts: 3,416
    edited 2014-09-13 - 11:42:07
    OK I figured out what was crashing FillCircle. The way I coded the repeat loop doesn't play well with zero. I recoded it thusly:
    pub sendMultiData(data, qty)
    
      if qty > 1
        pasm_mailbox := (qty - 1) << 8 + pmb_repeat
      sendData(data)
      repeat while pasm_mailbox <> 0
    

    ...and this fixes FillCircle. Cavelamb, could you see if this adjustment fixes your text crash too?

    Edit: I tried deliberately running text off the edge and it seems to work OK. The problem is that DrawVerticalLine is bound checked but if it's completely out of bounds the write length is zero, which was getting decremented to $FFFF_FFFF. So excluding zero from the repeat fixes the problem.

    And the whole image with the update: SS_TFT_PASMX.spin
  • cavelambcavelamb Posts: 687
    edited 2014-09-13 - 12:53:20
    That fixed the crash problem quite well.
    I tried the same thing, running off the edge.

    drawString(string("LiftOff!"),200,240,4,WHITE)

    All that prints is the "L" on the extreme right side.
    It looks like the text is probably trying to wrap around back to the left side.
    But not advancing past the first character (overwriting the previous chars?)
    It doesn't lock it up at all, so that's a graceful failure.

    edit:

    Also, just so you know, I moved the DAT sections to the end of the source file.
    for my project work.

    I think I vaguely remember that the DAT declaration will align on a long boundary?
    But if not, somebody holler!
  • David BetzDavid Betz Posts: 14,179
    edited 2014-09-13 - 13:23:03
    cavelamb wrote: »
    On the other topic, I may be wrong about this, but it seem to me that Spin IS the high level language for the Propeller.
    It's back to that whole Von-Gracey architecture thing.
    Spin is an interpreter-in-ROM, somewhat like the original Apple, TRS-80' etc computers of the olden days, but the
    whole machine was boiled down to a single chip (ok and eeprom, but you get the idea).
    It's a whole lot of power in a very tiny and unique package.
    It's FUN.
    Spin is a nice language. It may be the best language for programming the Propeller. Partly, this is because its 2K VM is in ROM so it doesn't waste space in hub memory. It also generates more compact code than C although it also tends to be slower as well. However, Spin is not a perfect language from my point of view. Objects are not first-class data types. It doesn't have any concept of structures. In its original form it had no mechanism for conditional compilation. I think even PBASIC has that. And, most of all, it's very slow thus requiring more code to be written in assembly language in order for a program to achieve decent performance. This isn't true of C on other CPU architectures. I also object to writing a program in a language that can only be run on a single type of processor and please don't say that all microcontroller code is processor specific anyway because the existence of this thread proves otherwise. Many people have ported C code from the Arduino to the Propeller with minimal changes. This just isn't going to be possible for code written in Spin going in the other direction. Of course, Parallax would probably rather that people didn't migrate from the Propeller to some other processor but I'd like to have that option should it become necessary for some reason. I have C code that I wrote back in 1983 that I can still run. It has been ported to numerous different processors without any significant problems. I'd like to continue to be able to do that and not end up building up a library of code that is stuck on a the Propeller. I'm not even totally convinced that Spin code for the P1 will be able to run on Spin for the P2 without rework.

    Anyway, Spin is fun. The Propeller is a lot of fun. I love both of them but if I'm going to write code that I hope to have a life beyond just one project I'd rather write it in a language that I have some hope will be supported on other processors and platforms.
  • DavidZemonDavidZemon Posts: 2,905
    edited 2014-09-13 - 14:18:45
    New numbers. All tests were run printing the text "Happy!" (I say this because I'm noticing significant differences in both C++ and Spin when the characters change - even with the same number of characters). PropWare::SeeedTFT is basically the original Arduino code, just ported to use some PropWare classes instead of Arduino. PropWare::SeeedTFTFast has been modified to use the PASM cog exactly as localroger posted in his example.

    PropWare::SeeedTFT w/ CMM
    - Init: 12.758 seconds
    - Text: 7.883 seconds

    PropWare::SeeedTFT w/ LMM
    - Init: 3.040 seconds
    - Text: 1.678 seconds

    PropWare::SeeedTFTFast w/ CMM
    - Init: 0.552 seconds
    - Text: 1.490 seconds

    PropWare::SeeedTFTFast w/ LMM
    - Init: 0.550 seconds
    - Text: 0.354 seconds

    SS_TFT_PASMR.spin
    - Init: 0.638 seconds
    - Text: 0.225 seconds

    These results sure do intrigue me! I have no idea why printing text from PropWare::SeeedTFTFast with LMM is so much slower than Spin.

    This link provides the exact commit with the code used in PropWare.
    And I've attached the Spin file that I used (I just added the Parallax Serial Terminal and some print statements)
  • localrogerlocalroger Posts: 3,416
    edited 2014-09-13 - 14:47:21
    That's fascinating, SwimDude. I wonder if the bottleneck in C text drawing is the overhead for calling FillRectangle for each drawn pixel. As I found when I tried manually macro-izing the Spin version there doesn't seem to be any noticeable overhead for calling a Spin method, but C might need to put more effort into building a stack frame for the passed parameters and local varaibles. C should be faster at the actual rectangle drawing for each pixel since it's faster at clearing the screen.

    It's not too surprising that different text changes the overhead because undrawn pixels are skipped, so it's really the number of pixels rather than the number of characters that determines the workload.
  • Peter JakackiPeter Jakacki Posts: 9,711
    edited 2014-09-13 - 21:23:19
    New numbers. All tests were run printing the text "Happy!" (I say this because I'm noticing significant differences in both C++ and Spin when the characters change - even with the same number of characters). PropWare::SeeedTFT is basically the original Arduino code, just ported to use some PropWare classes instead of Arduino. PropWare::SeeedTFTFast has been modified to use the PASM cog exactly as localroger posted in his example.

    PropWare::SeeedTFT w/ CMM
    - Init: 12.758 seconds
    - Text: 7.883 seconds

    PropWare::SeeedTFT w/ LMM
    - Init: 3.040 seconds
    - Text: 1.678 seconds

    PropWare::SeeedTFTFast w/ CMM
    - Init: 0.552 seconds
    - Text: 1.490 seconds

    PropWare::SeeedTFTFast w/ LMM
    - Init: 0.550 seconds
    - Text: 0.354 seconds

    SS_TFT_PASMR.spin
    - Init: 0.638 seconds
    - Text: 0.225 seconds

    These results sure do intrigue me! I have no idea why printing text from PropWare::SeeedTFTFast with LMM is so much slower than Spin.

    This link provides the exact commit with the code used in PropWare.
    And I've attached the Spin file that I used (I just added the Parallax Serial Terminal and some print statements)

    No grudge here, just curious how some of this code translates to Forth so I converted up to the init function and was quite surprised how "slow" it was. But then again I am not even using a little PASM module helper function, this is all bytecode, nothing fancy.
    Tachyon SS_TFT.fth
    - iNIT: 3.433 seconds
    - Text: (not converted)

    I can see that even with just a few longs of PASM in a RUNMOD module (same cog as Tachyon) that this should run a lot faster. I think there is a lot of time wasted with operands being pushed and popped. I also removed some redundancies and fixed what looked like what could be timing problems but I don't have one of these displays to play with. Interestingly I find SPI serial far more efficient than bit-bashing parallel data buses, I can transfer 16-bits of data in around 4.8us using the SPIWR instructions.


    {
      SeeedStudio Touchscreen TFT Driver
    
      This code is more or less directly translated from the Spin code by Jazzed 
      which was more or less directly translated from the Arduino source.
      Some code and timing optimizations made.
    
    }
    
    --- define some aliases to match the Spin code better
    
    ALIAS SHL    <<
    ALIAS SHR    >>
    
    ALIAS OUTCLR    LOW
    ALIAS OUTSET    HIGH
    
    
    DECIMAL
    
      11_1111_1100 == tftdat --- all pins
    
      '' Basic Colors
      $F800         == RED
      $07E0         == GREEN
      $001F         == BLUE
      $0000         == BLACK
      $FFE0         == YELLOW
      $FFFF         == WHITE
    
      '' Other Colors
      $07FF         == CYAN
      $F810         == BRIGHT_RED
      $8410         == GRAY1
      $4208         == GRAY2
    
      '' TFT resolution 240*320
      0             == MIN_X
      0             == MIN_Y
      240           == MAX_X
      320           == MAX_Y
    
      '' pin definitions from tft.h.  The pin manipulation
      $04 8 <<    == CS
      $08 8 <<    == RS
      $10 8 <<    == WR
      $20 8 <<    == RD
    
      '' Macro definitions for char display direction
      0        == LEFT2RIGHT
      1        == DOWN2UP
      2        == RIGHT2LEFT
      3        == UP2DOWN
    
    
      byte DisplayDirect
    
    ( define a couple of words to make the tables look more like the Spin version )
    pub GETCSV            "," delim C! GETWORD NUMBER ;
    pub bytes IMMEDIATE        BEGIN GETCSV WHILE codes W@ C! DROP 1 ALLOT REPEAT ;
    pub words IMMEDIATE        BEGIN GETCSV WHILE codes W@ W! DROP 2 ALLOT REPEAT ;
    
    
    ( Table includes delays and EOT codes )
    TABLE InitSeq
        words $FFFF,#0100,
        words $0001,$0100,$0002,$0700,$0003,$1030,$0004,$0000,
        words $0008,$0302,$000A,$0000,$000C,$0000,$000D,$0000,$000F,$0000,
        words $FFFF,#0100,
        words $0030,$0000,$0031,$0405,$0032,$0203,$0035,$0004,$0036,$0B07,
        words $0037,$0000,$0038,$0405,$0039,$0203,$003C,$0004,$003D,$0B07,
        words $0020,$0000,$0021,$0000,$0050,$0000,$0051,$00EF,$0052,$0000,
        words $0053,$013F,$FFFF,#0100,
        words $0060,$A700,$0061,$0001,$0090,$003A,$0095,$021E,$0080,$0000,
        words $0081,$0000,$0082,$0000,$0083,$0000,$0084,$0000,$0085,$0000,
        words $00FF,$0001,$00B0,$140D,$00FF,$0000,
        words $FFFF,#0100,$FFFF,#0000,    
        
    
    pub getData
      1 ms P@ 2 >> >B
    ;
    
    pub pushData ( byte --- ) --- 10.8us
        tftdat LOW
        2 << HIGH 
        CLOCK CLOCK --- strobe the WR line low high
    ;
    
    
    
    pub sendData ( data -- ) --- 35.6us
    \\\    RS HIGH ( redundant)
    pri send16
    \\\ RD HIGH ( redundant)
        CS LOW
        W>B pushData pushData --- WriteData (16-bits)
        CS HIGH
    ;
    pub sendCommand ( index -- ) --- 45.2us
        RS LOW send16 
        RS HIGH --- returns RS back to default data select  
    ;
    
    
    pub readRegister ( index -- data )
        sendCommand
        
        tftdat INPUTS --- place data bus into read state
    \\\    RS HIGH ( redundant)
        --- strobe RD and read data
        RD LOW RD HIGH getData 8 <<
        RD LOW RD HIGH getData OR
        CS HIGH
    ;
    
    pri SendCmdSeq ( ptr -- ) 
      BEGIN
        DUP W@ --- fetch a code from the table
        $FFFF = 
        IF ( sequence control code ) 
          2+ DUP W@ --- read next word as either a delay or an end of table (0) 
          ?DUP IF ms ELSE DROP EXIT THEN 
        ELSE
          DUP W@ sendCommand
          2+ DUP W@ sendData
        THEN
        2+
      AGAIN
    ;
    
    
    
    pub exitStandBy
        $0010 sendCommand
        $14E0 sendData
        100 ms
        $0007 sendCommand
        $0133 sendData
    ;
    pub init  --- 3.433sec
        CS HIGH        --- set control pins as inactive outputs
        RD HIGH
        WR HIGH
        RS HIGH
        tftdat LOW
        WR 4 COGREG!    --- setup WR as a STROBE op (slightly faster)
        InitSeq SendCmdSeq  --- 403.6ms (inc 400ms of delays)
    
        $0007 sendCommand
        $0133 sendData
        50 ms
        exitstandby
        $0022 sendCommand
    --- init cascades into the clearScreen function
    pub clearScreen --- 4.8sec ; 3.7sec using CLOCK CLOCK for WR strobe ; 3.1sec by disabling redundant ops
          MAX_X MAX_Y * FOR BLACK sendData NEXT
    ;
    

  • Heater.Heater. Posts: 21,233
    edited 2014-09-14 - 00:34:21
    SwimDude0614,

    I don't quite follow what you have done. You have results:

    PropWare::SeeedTFTFast w/ CMM
    - Init: 0.552 seconds
    - Text: 1.490 seconds

    PropWare::SeeedTFTFast w/ LMM
    - Init: 0.550 seconds
    - Text: 0.354 seconds

    SS_TFT_PASMR.spin
    - Init: 0.638 seconds
    - Text: 0.225 seconds

    and then say "
    I have no idea why printing text with LMM is so much slower than Spin."

    Now the name of the Spin file implies it uses PASM where as there is no mention of PASM for the C files.

    That implies the Spin code contains PASM to do the heavy lifting but the C code does not.

    Is this the case or not? If it is you are not comparing Spin and C but rather PASM and C.




  • David BetzDavid Betz Posts: 14,179
    edited 2014-09-14 - 04:11:56
    Heater. wrote: »
    SwimDude0614,

    I don't quite follow what you have done. You have results:

    PropWare::SeeedTFTFast w/ CMM
    - Init: 0.552 seconds
    - Text: 1.490 seconds

    PropWare::SeeedTFTFast w/ LMM
    - Init: 0.550 seconds
    - Text: 0.354 seconds

    SS_TFT_PASMR.spin
    - Init: 0.638 seconds
    - Text: 0.225 seconds

    and then say "
    I have no idea why printing text with LMM is so much slower than Spin."

    Now the name of the Spin file implies it uses PASM where as there is no mention of PASM for the C files.

    That implies the Spin code contains PASM to do the heavy lifting but the C code does not.

    Is this the case or not? If it is you are not comparing Spin and C but rather PASM and C.




    I think he modified the C code to call the PASM helpers:
    PropWare::SeeedTFTFast has been modified to use the PASM cog exactly as localroger posted in his example.
  • DavidZemonDavidZemon Posts: 2,905
    edited 2014-09-14 - 08:35:01
    David Betz wrote: »
    I think he modified the C code to call the PASM helpers:

    Exactly
  • This whole thread seems to be comparing apples to oranges.
    I'm not sure what's going on with the various versions posted above, but it's pretty easy to do an apples-to-apples comparison: just convert the Spin code to C++ with spin2cpp. I had to change the name of the Parallax Serial Terminal file to do that, but that's the only change needed to the spin file. The results are what I would expect: C++ in CMM mode is twice as fast as Spin at text drawing but is larger:
    Spin + PASM:
    
    compiled with openspin SS_TFT_PASMR.spin
    
    size: 3868 bytes
    Initialization time: 638 ms
    Text print time: 225 ms
    
    CMM + PASM:
    
    compiled with spin2cpp --binary -Os -mcmm SS_TFT_PASMR.spin
    
    size: 7236 bytes (5700 bytes w/o kernel)
    Initialization time: 635 ms
    Text print time: 106 ms
    
    LMM + PASM:
    
    compiled with spin2cpp --binary -Os -mlmm SS_TFT_PASMR.spin
    
    size: 11308 bytes (9996 bytes w/o kernel)
    Initialization time: 550 ms
    Text print time: 26 ms
    

    I've attached the code I used, along with the spin2cpp output and the binaries.
  • I seem to have done a crap job coding PropWare::SeeedTFT and PropWare::SeeedTFTFast. I can't replicate the results at all. I think @ersmith has the best approach.
Sign In or Register to comment.