Shop OBEX P1 Docs P2 Docs Learn Events
PSRAM example — Parallax Forums

PSRAM example

Hello,

I took a look at this demo and wanted to try to interact with the P2 edge's 32MB of Ram:

https://forums.parallax.com/discussion/176083/3d-teapot-demo/p1

I did read some of the code in this thread and referenced the manual. The program does compile but I feel like i'm missing something to write the letter A to the psram and then read it back:

' sets the system clock frequency
_CLKFREQ = 180_000_000
' set the receiving pin for input to the P2 microcontroller
RX_PIN = 63
' set the transmission pin for output from the P2 microcontroller
TX_PIN = 62
' set the baud mode to support 2000000 baud
BAUD_RATE = 2_000_000
' set the serial clock period for the baud rate based on the system clock frequency (_CLKFREQ)
SERIAL_CLOCK = ( _CLKFREQ / BAUD_RATE )
' calculate a bit period value for the serial communication baud rate.
BIT_PERIOD = (( SERIAL_CLOCK << 16 ) & $FFFFFC00 ) + ( 8 - 1 )

' PSRAM Pin Definitions
P_PSRAM_D       = 40        ' Data bus (4-bit, P40-P43)
P_PSRAM_CLK     = 56        ' PSRAM Clock
P_PSRAM_CE      = 57        ' PSRAM Chip Enable

' Constants
PSRAM_ADDR      = $000000   ' Address to store 'A' in PSRAM

dat
    org       0   ' start assembling code from address 0
    asmclk     ' use the system clock (_CLKFREQ) for timing-related calculations 

    ' Configure RX smart pin
    wrpin    ##(P_ASYNC_RX | P_HIGH_15K), #RX_PIN  ' configure the RX pin as a smart pin for asynchronous serial reception
    wxpin    ##BIT_PERIOD, #RX_PIN             ' set the correct timing for receiving bits at the correct speed
    wypin    #1, #RX_PIN                      ' Enable the smart pin
    dirh     #RX_PIN                          ' sets the direction of the RX pin to high

    ' Configure TX smart pin
    wrpin    ##(P_ASYNC_TX | P_OE), #TX_PIN    ' configure the TX pin as a smart pin for asynchronous serial transmission 
    wxpin    ##BIT_PERIOD, #TX_PIN             ' set the correct timing for transmitting bits at the correct speed
    wypin    #1, #TX_PIN                      ' Enable the smart pin transmitter
    dirh     #TX_PIN                          ' sets the direction of the TX pin to high

.start
    ' Initialize system
    call    #.psram_init      ' Initialize PSRAM

    ' Write letter 'A' to PSRAM
    mov     tmp1, #$41
    call    #.write_psram

    ' Read back letter 'A' from PSRAM
    call    #.read_psram

    ' Send output to PST
    call    #.uart_tx_char

    ' End program loop
    jmp    #.done

'------------------------------------------------------------
' Initialize PSRAM
'------------------------------------------------------------
.psram_init
    ' Activate PSRAM to prepare for operations
    drvl    #P_PSRAM_CE   ' Activate PSRAM (low active)
    ret

'------------------------------------------------------------
' Write to PSRAM (Full 8-bit mode)
'------------------------------------------------------------
.write_psram
    ' Set address (PSRAM_ADDR = 0x000000)
    mov     tmp2, #PSRAM_ADDR

    ' Enable PSRAM
    drvl    #P_PSRAM_CE

    ' Write Data (8-bit split into 4-bit nibbles)
    mov     tmp0, tmp1        ' Copy the 8-bit value
    and     tmp1, ##$F      ' Mask the lower 4 bits (lower nibble)
    mov     outa, tmp1
    wrpin   ##%01, #P_PSRAM_D ' Configure P40-P43 as outputs
    drvl    #P_PSRAM_CLK
    call    #.clock_pulse
    drvh    #P_PSRAM_CLK

    ' Write second nibble (upper 4 bits)
    shr     tmp0, #4        ' Shift right by 4 to get the upper nibble
    and     tmp0, ##$F      ' Mask the upper 4 bits
    mov     outa, tmp0
    wrpin   ##%01, #P_PSRAM_D ' Configure P40-P43 as outputs
    drvl    #P_PSRAM_CLK
    call    #.clock_pulse
    drvh    #P_PSRAM_CLK

    ' Disable PSRAM
    drvh    #P_PSRAM_CE
    ret

'------------------------------------------------------------
' Read from PSRAM (Full 8-bit mode)
'------------------------------------------------------------
.read_psram
    ' Enable PSRAM
    drvl    #P_PSRAM_CE

    ' Set Data Bus as Input
    wrpin   ##%00, #P_PSRAM_D  ' Configure P40-P43 as inputs
    dirl    #P_PSRAM_D        ' Set direction to input

    ' Clock Pulse Before Reading
    call    #.clock_pulse

    ' Read lower nibble (first 4 bits)
    rdpin   tmp3, #P_PSRAM_D    ' Read data from P40-P43
    and     tmp3, ##$F          ' Mask only the 4-bit value

    ' Shift to make room for the upper nibble
    shl     tmp3, #4        ' Shift left to clear the lower nibble

    ' Clock Pulse Before Reading Upper nibble
    call    #.clock_pulse

    ' Read upper nibble (next 4 bits)
    rdpin   tmp4, #P_PSRAM_D    ' Read data from P40-P43
    and     tmp4, ##$F          ' Mask only the 4-bit value
    or      tmp3, tmp4        ' Combine the two nibbles (upper + lower)

    ' Send the read value for debugging
    call    #.uart_tx_char   ' Send value through UART

    ' Disable PSRAM
    drvh    #P_PSRAM_CE
    ret

.data_error
    ' Handle the error (e.g., signal failure via UART or LED)
    call    #.uart_tx_char   ' Send an error message
    ret


'------------------------------------------------------------
' UART Send Character (t3 contains data)
'------------------------------------------------------------
.uart_tx_char
    wypin   tmp3, #TX_PIN  ' Send character
    nop
    waitx   ##5000       ' Small delay for stability
    ret

'------------------------------------------------------------
' Clock Pulse Routine
'------------------------------------------------------------
.clock_pulse
    drvl    #P_PSRAM_CLK
    nop
    drvh    #P_PSRAM_CLK
    ret

.done


'------------------------------------------------------------
' Variable Registers
'------------------------------------------------------------
tmp0              res 1       ' 
tmp1              res 1       ' 
tmp2              res 1       ' 
tmp3              res 1       ' 
tmp4              res 1       ' 

Comments

  • It doesn't work quite so easily:

    1. You need to initialize the PSRAM chips. They start out in 1-bit mode and need to be brought into 4-bit mode
    2. You can't use RDPIN to read multiple pins, it just doesn't work like that
    3. You need to send a command to the chip first (it needs to know which address to read/write)
    4. You can't be too slow working with the PSRAM. It can only refresh rows if CE is high, so if it's low for too long you run the risk of loosing some bits. Ideally you use streamer commands, which can work at the maximum possible speed (which is one bus transfer every 2 cycles)

    See also:

  • shannon1949shannon1949 Posts: 8
    edited 2025-03-18 22:05

    I have had great success with psram.spin2 and psram16drv.spin2 found at

    https://www.parallax.com/package/p2-solar-panel-code/

    This driver pair allows burst reads and writes; longer reads and writes achieve greater efficiency. For example, moving 64 longs at at time yields 100 MB/s. Huge thanks to Michael Mulholland for developing these drivers! (I use flexspin/flexprop, which may be required, not sure.)

  • proppyproppy Posts: 22
    edited 2025-03-19 19:04

    This was all helpful to get me a little closer.

    '------------------------------------------------------------
    ' Constants and Pin Definitions
    '------------------------------------------------------------
    _CLKFREQ = 180_000_000   ' Set system clock frequency
    RX_PIN = 63               ' Set the receiving pin for input to the P2 microcontroller
    TX_PIN = 62               ' Set the transmission pin for output from the P2 microcontroller
    BAUD_RATE = 2_000_000     ' Set the baud rate for serial communication
    SERIAL_CLOCK = (_CLKFREQ / BAUD_RATE)  ' Set the serial clock period based on the system clock frequency
    BIT_PERIOD = ((SERIAL_CLOCK << 16) & $FFFFFC00) + (8 - 1)  ' Calculate bit period for baud rate
    
    ' PSRAM Pin Definitions
    P_PSRAM_D = 40            ' Data bus (4-bit, P40-P43)
    P_PSRAM_CLK = 56          ' PSRAM Clock
    P_PSRAM_CE = 57           ' PSRAM Chip Enable
    
    ' Constants
    PSRAM_ADDR = $000000     ' Address to store 'A' in PSRAM
    
    '------------------------------------------------------------
    ' Initialize system and Smart Pin configurations
    '------------------------------------------------------------
    dat
        org 0
        asmclk                  ' Use the system clock (_CLKFREQ) for timing-related calculations 
    
        ' Configure RX smart pin
        wrpin ##(P_ASYNC_RX | P_HIGH_15K), #RX_PIN  ' Configure RX pin for asynchronous serial reception
        wxpin ##BIT_PERIOD, #RX_PIN             ' Set correct timing for receiving bits
        wypin #1, #RX_PIN                      ' Enable RX pin smart pin
        dirh #RX_PIN                          ' Set RX pin as input
    
        ' Configure TX smart pin
        wrpin ##(P_ASYNC_TX | P_OE), #TX_PIN    ' Configure TX pin for asynchronous serial transmission 
        wxpin ##BIT_PERIOD, #TX_PIN             ' Set correct timing for transmitting bits
        wypin #1, #TX_PIN                      ' Enable TX pin smart pin
        dirh #TX_PIN                          ' Set TX pin as output
    
    .start
        ' Initialize system
        call #.psram_init     ' Initialize PSRAM
    
    
        mov tmp1, #$38  ' Command to QPI write
        call #.psram_send_command
    
        ' Write letter 'A' to PSRAM
        mov tmp1, #"A"        ' ASCII code for 'A'
        mov tmp2, #PSRAM_ADDR ' Set address to start writing
    
        ' Write the letter to PSRAM
        call #.write_psram
    
        ' Wait for a brief moment
        waitx ##100000
    
        mov tmp1, #$EB  ' Command to enter QPI read
        call #.psram_send_command
        ' Read back letter 'A' from PSRAM
        call #.read_psram
    
        ' Send output to PST (UART)
        call #.uart_tx_char
    
        ' End program loop
        jmp #.done
    
    '------------------------------------------------------------
    ' Initialize PSRAM to Quad SPI Mode (QPI)
    '------------------------------------------------------------
    .psram_init
        ' Reset Enable Command (Optional)
        mov tmp1, #$66  ' Command to enable reset
        call #.psram_send_command
    
        ' Reset Command (Optional)
        mov tmp1, #$99  ' Command to reset PSRAM
        call #.psram_send_command
    
        ' Enter Quad Mode Command
        mov tmp1, #$35  ' Command to enter QPI mode (Quad SPI mode)
        call #.psram_send_command
    
         ' Wait for a brief moment to ensure that PSRAM has transitioned
          waitx ##100000
    
        ret
    
    '------------------------------------------------------------
    ' Send Command to PSRAM (Uses tmp1 as the command)
    '------------------------------------------------------------
    .psram_send_command
        ' Activate PSRAM to prepare for command
        drvl #P_PSRAM_CE      ' Enable PSRAM chip (CE low)
    
        ' Send the command (this assumes the command is in tmp1)
        mov tmp2, tmp1        ' Copy command to tmp2
    
        ' Use appropriate smart pin mode to send command
        ' Send 8-bit command in Serial mode (DIO)
        wrpin ##$04, #P_PSRAM_D  ' Set Data pins for output
        dirh #P_PSRAM_D        ' Set pins as output
        wypin tmp2, #P_PSRAM_D  ' Send the command byte to PSRAM
    
        ' Clock Pulse After Sending Command
        call #.clock_pulse
    
        ' Disable PSRAM chip
        drvh #P_PSRAM_CE       ' Disable PSRAM chip (CE high)
    
        ret
    
    '------------------------------------------------------------
    ' Write to PSRAM 
    '------------------------------------------------------------
    .write_psram
        ' Enable PSRAM chip
        drvl #P_PSRAM_CE
    
        ' Write Data to PSRAM
        wrpin ##$00, #P_PSRAM_D  ' Set Data pins for output
        dirh #P_PSRAM_D          ' Set pins as output
        wypin tmp1, #P_PSRAM_D    ' Write the byte to PSRAM
    
        ' Clock pulse after write
        call #.clock_pulse
    
        ' Disable PSRAM chip
        drvh #P_PSRAM_CE
        ret
    
    '------------------------------------------------------------
    ' Read from PSRAM 
    '------------------------------------------------------------
    .read_psram
        ' Enable PSRAM chip
        drvl #P_PSRAM_CE
    
        ' Set Data Bus as Input (P40-P43)
        wrpin ##$00, #P_PSRAM_D  ' Configure P40-P43 as inputs
        dirl #P_PSRAM_D          ' Set direction to input
    
        ' Clock Pulse Before Reading
        call #.clock_pulse
    
        'this is a test
        'mov tmp3, #"A"
    
        ' Read the data
        'rdbyte tmp3, #P_PSRAM_D   ' Read data from PSRAM
        rdbyte tmp3, #P_PSRAM_D   ' Read data from PSRAM
    
        ' Disable PSRAM chip
        drvh #P_PSRAM_CE
        ret
    
    '------------------------------------------------------------
    ' UART Send Character (t3 contains data)
    '------------------------------------------------------------
    .uart_tx_char
        wypin tmp3, #TX_PIN     ' Send character via UART
        nop
        waitx ##5000            ' Small delay for stability
        ret
    
    '------------------------------------------------------------
    ' Clock Pulse Routine
    '------------------------------------------------------------
    .clock_pulse
        drvl #P_PSRAM_CLK
        nop
        drvh #P_PSRAM_CLK
        ret
    
    .done
    ' End of program loop
    
    '------------------------------------------------------------
    ' Variable Registers
    '------------------------------------------------------------
    tmp0 res 1
    tmp1 res 1
    tmp2 res 1
    tmp3 res 1
    
    
    

    Right now, i fixed things so the output at least works. I get the character ?, but i still need to do the streamer. Also replaced the rdpin's with some rdbytes an the same for the writing. I tested the read a bit to make sure it at least works with these lines:
    'this is a test
    'mov tmp3, #"A"

    ' Read the data
    rdbyte tmp3, #P_PSRAM_D   ' Read data from PSRAM
    

    At this point in the code, it prints A if i uncomment the mov, otherwise I get the ?. The PSRAM's datasheet was extremely helpful. I'll keep plugging away at it though.

  • Wuerfel_21Wuerfel_21 Posts: 5,266
    edited 2025-03-19 19:16

    RDBYTE is also not the correct instruction for this.
    You want GETBYTE x, INA+(P_PSRAM_D/32),#(P_PSRAM_D/8)&3. That will grab a byte from the I/O port.
    Though I would recommend getting a grip on the basics of PASM programming before trying to mess with something harder like the PSRAM.

  • That might not be a bad idea, will do. Thank you!

  • RaymanRayman Posts: 15,029

    @cgracey made a super simple PSRAM driver. He is using it for VGA in this thread:
    https://forums.parallax.com/discussion/175725/anti-aliased-24-bits-per-pixel-hdmi/p1

  • @Rayman

    Wow, I’m gonna bookmark this and have a closer look. I took a quick scan of it and noticed it has all the parts in here and even the streamer. I might give it another shot. Thank you!!!

    After this though I might go back to some of the simpler add ons and general assembly language as suggested. I think I got ahead of myself when I was able a lot things working so quickly.

  • evanhevanh Posts: 16,336

    It looks like there is a bug with the number of clocks generate in Chip's driver code. I've not tested it but it looks like the add pb,len should be a mov pb,len instead. Amusingly it probably doesn't really matter a lot since the clock can probably be left running unbroken anyway.

Sign In or Register to comment.