Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Synchronous PSRam access

Status
Not open for further replies.

Leep

Junior Member level 3
Joined
Oct 20, 2011
Messages
27
Helped
1
Reputation
2
Reaction score
1
Trophy points
1,283
Activity points
1,678
I'm trying to learn how to write and read the cellular ram (PSRam) on the Nexys 3 board. I've read the Nexys 3 Reference Manual, did a TON of Google searching (learned what PSRam was and probably about 7 or 8 other new terms), and finally managed to find some sample Verilog code and tweaked/modified it to get it to work. It does asynchronous writes/reads. Unfortunately, according to the ref manual, it (the PSRam on the Nexys 3) requires "read and write cycle times of 70ns" when operating in asynchronous mode which comes out to around 14MHz. Since I'm learning this so I can continue learning how to make a video controller and I want to read pixel data from RAM at 25.175MHz, it's a bit too slow.

Fortunately, the ref manual also states that in synchronous mode, "continuous transfers of up to 80MHz are possible". Unfortunately, I can't figure out how to do synchronous write/reads. I'm finding it very difficult to find many examples of how to access PSRam at all, much less synchronously and in Verilog. VHDL confuses the mess out of me... I need to learn it so I have access to more examples, but it's on a long list of "things to learn".

Can anyone throw some pointers my way on how to do synchronous PSRam access? BTW, I tried to cheat until I learned RAM access and just give myself an array of 640*480 8-bit registers... too large for the device (something about not enough SLICEM's). :)

-----
Lee
 

I forgot to post the code I have working for asynchronous PSRam access, just so anyone else looking for how to do this can run across it in the future. :) Also, I welcome any comments (positive or negative).

psram_test.v
Code:
module psram_test
  (
    input  wire clk,
    output wire [7:0]  Led,
    input  wire [2:0]  btn, // 0=Write switches to address 0, 1 = display contents of address 0 on LEDs, 2 = clear LEDs
    input  wire [7:0]  sw,
    output wire        MemOE,
    output wire        MemWR,
    output wire        MemAdv,
    output wire        RamCS,
    output wire        MemClk,
    output wire        RamLB,
    output wire        RamUB,
    output wire [23:1] MemAdr,
    inout  wire [15:0] MemDB
  );

//Registers
reg [31:0] state = 0;
reg [7:0]  rLed;

reg rMemOE = 1;
reg rMemWR = 1;
reg [15:0] rMemDB;
reg [23:1] rMemAdr;

localparam CLOCK_BIT = 3; // clkSlower[3] will be a 12.5MHz clock
reg [CLOCK_BIT:0] clkSlower;

//Static assignments
assign RamCS = 0;   //chip select is always low (enabled)
assign MemClk = 0;  //clk is disabled in asynchronous mode
assign MemAdv = 0;  //address valid can always be pulled low in asynchronous mode
assign RamLB = 0;   //lower byte is enabled
assign RamUB = 0;   //upper byte is enabled

//Register assignments
assign MemOE = rMemOE;
assign MemWR = rMemWR;
assign MemDB[15:0] = rMemDB[15:0];
assign MemAdr[23:1] = rMemAdr[23:1];

assign Led[7:0] = rLed[7:0];

always @(posedge clk)
begin
  clkSlower <= clkSlower + 1'b1;
end

always @(posedge clkSlower[CLOCK_BIT])
begin
  if(btn[0])                      //btn[0] is reset/write
  begin
    state <= 0;
  end else if(btn[1])             //btn[1] is read
  begin
    state <= 50;
  end else if(btn[2])             //btn[2] clears Leds
  begin
    rLed <= 8'b0;
  end else begin
    case(state)
      0: begin
        rMemAdr <= 23'b0;         //set address to 0
        rMemDB <= sw;             //write switches to the data bus
        rMemWR <= 0;              //pull write enable low to write the data
        state <= 10;      
      end
      10: begin
        rMemWR <= 1;              //pull write enable to high again
      end
      50: begin
        rMemDB <= 16'bzzzzzzzzzzzzzzzz; //set the data bus to high impedance
        rMemOE <= 0;                    //pull output enable low to start a read
        state <= 60;
      end
      60: begin
        rLed[7:0] <= MemDB[7:0];  //set the LEDs to show the data
        state <= 70;
      end
      70: begin
        rMemOE <= 1;              //pull output enable high again
      end
    endcase
  end
end

endmodule

psram_text.ucf (for the Nexys 3)
Code:
Net "clk" LOC=V10 | IOSTANDARD=LVCMOS33;
Net "clk" TNM_NET = sys_clk_pin;
TIMESPEC TS_sys_clk_pin = PERIOD sys_clk_pin 100000 kHz;

Net "MemOE" LOC = L18 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L46N_FOE_B_M1DQ3, Sch name = P30-OE
Net "MemWR" LOC = M16 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L47P_FWE_B_M1DQ0, Sch name = P30-WE
Net "MemAdv" LOC = H18 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L43N_GCLK4_M1DQ5, Sch name = P30-ADV
Net "MemClk" LOC = R10 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L29P_GCLK3, Sch name = P30-CLK
Net "RamCS" LOC = L15 | IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L42P_GCLK7_M1UDM, Sch name = MT-CE
Net "RamUB" LOC = K15  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L41P_GCLK9_IRDY1_M1RASN, Sch name = MT-UB
Net "RamLB" LOC = K16  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L41N_GCLK8_M1CASN, Sch name = MT-LB

Net "MemAdr<1>" LOC = K18  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L45N_A0_M1LDQSN, Sch name = P30-A0
Net "MemAdr<2>" LOC = K17  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L45P_A1_M1LDQS, Sch name = P30-A1
Net "MemAdr<3>" LOC = J18  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L44N_A2_M1DQ7, Sch name = P30-A2
Net "MemAdr<4>" LOC = J16  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L44P_A3_M1DQ6, Sch name = P30-A3
Net "MemAdr<5>" LOC = G18  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L38N_A4_M1CLKN, Sch name = P30-A4
Net "MemAdr<6>" LOC = G16  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L38P_A5_M1CLK, Sch name = P30-A5
Net "MemAdr<7>" LOC = H16  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L37N_A6_M1A1, Sch name = P30-A6
Net "MemAdr<8>" LOC = H15  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L37P_A7_M1A0, Sch name = P30-A7
Net "MemAdr<9>" LOC = H14  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L36N_A8_M1BA1, Sch name = P30-A8
Net "MemAdr<10>" LOC = H13  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L36P_A9_M1BA0, Sch name = P30-A9
Net "MemAdr<11>" LOC = F18  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L35N_A10_M1A2, Sch name = P30-A10
Net "MemAdr<12>" LOC = F17  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L35P_A11_M1A7, Sch name = P30-A11
Net "MemAdr<13>" LOC = K13  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L34N_A12_M1BA2, Sch name = P30-A12
Net "MemAdr<14>" LOC = K12  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L34P_A13_M1WE, Sch name = P30-A13
Net "MemAdr<15>" LOC = E18  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L33N_A14_M1A4, Sch name = P30-A14
Net "MemAdr<16>" LOC = E16  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L33P_A15_M1A10, Sch name = P30-A15
Net "MemAdr<17>" LOC = G13  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L32N_A16_M1A9, Sch name = P30-A16
Net "MemAdr<18>" LOC = H12  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L32P_A17_M1A8, Sch name = P30-A17
Net "MemAdr<19>" LOC = D18  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L31N_A18_M1A12, Sch name = P30-A18
Net "MemAdr<20>" LOC = D17  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L31P_A19_M1CKE, Sch name = P30-A19
Net "MemAdr<21>" LOC = G14  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L30N_A20_M1A11, Sch name = P30-A20
Net "MemAdr<22>" LOC = F14  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L30P_A21_M1RESET Sch name = P30-A21
Net "MemAdr<23>" LOC = C18  |  IOSTANDARD = LVCMOS33; #Bank = 1, pin name = IO_L29N_A22_M1A14, Sch name = P30-A22

Net "MemDB<0>" LOC = R13 |  IOSTANDARD = LVCMOS33; #Ram or Numonyx Paralell Flash DB<0>, or Dual/Quad SPI Flash DB<1>, Bank = MISC, pin name = IO_L3P_D0_DIN_MISO_MISO1_2, Sch name = P30-DQ0
Net "MemDB<1>" LOC = T14 |  IOSTANDARD = LVCMOS33; #Ram or Numonyx Paralell Flash DB<1>, or Quad SPI Flash DB<2>, Bank = MISC, pin name = IO_L12P_D1_MISO2_2, Sch name = P30-DQ1
Net "MemDB<2>" LOC = V14 |  IOSTANDARD = LVCMOS33; #Ram or Numonyx Paralell Flash DB<2>, or Quad SPI Flash DB<3>, Bank = MISC, pin name = IO_L12N_D2_MISO3_2, Sch name = P30-DQ2
Net "MemDB<3>" LOC = U5  |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_49P_D3, Sch name = P30-DQ3
Net "MemDB<4>" LOC = V5  |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_49N_D4, Sch name = P30-DQ4
Net "MemDB<5>" LOC = R3  |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L62P_D5, Sch name = P30-DQ5
Net "MemDB<6>"  LOC = T3  |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L62N_D6, Sch name = P30-DQ6
Net "MemDB<7>"  LOC = R5  |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L48P_D7, Sch name = P30-DQ7
Net "MemDB<8>"  LOC = N5  |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L64P_D8, Sch name = P30-DQ8
Net "MemDB<9>"  LOC = P6  |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L64N_D9, Sch name = P30-DQ9
Net "MemDB<10>"  LOC = P12 |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L13N_D10, Sch name = P30-DQ10
Net "MemDB<11>"  LOC = U13 |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L14P_D11, Sch name = P30-DQ11
Net "MemDB<12>"  LOC = V13 |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L14N_D12, Sch name = P30-DQ12
Net "MemDB<13>"  LOC = U10 |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L30P_GCLK1_D13, Sch name = P30-DQ13
Net "MemDB<14>"  LOC = R8  |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L31P_GCLK31_D14, Sch name = P30-DQ14
Net "MemDB<15>"  LOC = T8  |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L31N_GCLK30_D15, Sch name = P30-DQ15

Net "led<0>" LOC = U16 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L2P_CMPCLK, Sch name = LD0
Net "led<1>" LOC = V16 |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L2N_CMPMOSI, Sch name = LD1
Net "led<2>" LOC = U15 |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L5P, Sch name = LD2
Net "led<3>" LOC = V15 |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L5N, Sch name = LD3
Net "led<4>" LOC = M11 |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L15P, Sch name = LD4
Net "led<5>" LOC = N11 |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L15N, Sch name = LD5
Net "led<6>" LOC = R11 |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L16P, Sch name = LD6
Net "led<7>" LOC = T11 |  IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L16N_VREF, Sch name = LD7

Net "sw<0>" LOC = T10 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L29N_GCLK2, Sch name = SW0
Net "sw<1>" LOC = T9 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L32P_GCLK29, Sch name = SW1
Net "sw<2>" LOC = V9 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L32N_GCLK28, Sch name = SW2
Net "sw<3>" LOC = M8 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L40P, Sch name = SW3
Net "sw<4>" LOC = N8 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L40N, Sch name = SW4
Net "sw<5>" LOC = U8 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L41P, Sch name = SW5
Net "sw<6>" LOC = V8 | IOSTANDARD = LVCMOS33; #Bank = 2, pin name = IO_L41N_VREF, Sch name = SW6
Net "sw<7>" LOC = T5 | IOSTANDARD = LVCMOS33; #Bank = MISC, pin name = IO_L48N_RDWR_B_VREF_2, Sch name = SW7

Net "btn<1>" LOC = B8 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L33P, Sch name = BTNS
Net "btn<0>" LOC = C4 | IOSTANDARD = LVCMOS33; #Bank = 0, pin name = IO_L1N_VREF, Sch name = BTNL
Net "btn<2>" LOC = D9 | IOSTANDARD = LVCMOS33; # Bank = 0, pin name = IO_L34P_GCLK19, Sch name = BTNR
 
I just realized the asynchronous mode of the PSRam is reading/writing 16 bits at a time, so I'm getting 2 bytes at a time. So If I read 2 bytes (2 pixels @ 8-bits each) every other clock cycle of my 25MHz clock (12.5 MHz), I can read the data fast enough to update the screen from it. Not sure how that works if other "processes" are trying to read/write from the memory... If something is accessing the memory in the clock cycle I need to access it and i need to wait clock cycles before I can read then I won't get the data quick enough. I'm familiar with multi-threaded concerns in the software world, but not at all with the equivalent in the hardware world. SO much still to learn. :|

I still need to learn how to do the synchronous access so I can read/write at up to 80MHz (instead of ~14Mhz), so any tips/pointers... I'm getting close to deciphering the control codes used to access the PSRam... I've found them in a comment in some code I found, but not "officially" documented anywhere.

Code:
        // --------------------------------------------------------------------------
        //                                          98 7654321098 7 65 4 3 210 
        //                                          |    |        |  | | |  |
        //  FIRST   *[B][COLOR="#FF0000"]RCR[/COLOR][/B] REG DEF*                   |    |        |  | | |  --------------> PAR 
        //  LOOK                                    |    |        |  | | |                  000 => Full array (default)
        //  HERE                                    |    |        |  | | |                  001 => Bottom 1/2
        //   |                                      |    |        |  | | -> Reserved        010 => Bottom 1/4
        //   |                                      |    |        |  | |                    011 => Bottom 1/8
        //   |        Register Select  <-------------    |        |  | -> Deep Pwr Down     100 => None
        //   |        [B][COLOR="#FF0000"]00=RCR[/COLOR][/B]                             |        |  |    0=Deep Pwr Dn.    101 => Top 1/2
        //   -------->10=BCR                             |        |  |    1=Off (default)   110 => Top 1/4
        //            01=DIDR *CR1.5* ONLY               |        |  |                      111 => Top 1/8
        //                                               |        |  -> Reserved           
        //                                               |        |              
        //                                               |        --> Page Mode
        //                                               |            0-Disabled
        //               Reserved set to 0 <--------------            1-Enabled
        //                


        // --------------------------------------------------------------------------
        //                                          98 76 5 4 321 0 9 8 76 54 3 210 
        //                                          |  |  | |  |  | | |  | |  |  |               
        //  FIRST   *[B][COLOR="#FF0000"]BCR[/COLOR][/B] REG DEF*                   |  |  | |  |  | | |  | |  |  --------------->[B][COLOR="#FF0000"]Burst Length[/COLOR][/B]:
        //  LOOK                                    |  |  | |  |  | | |  | |  |                  001    =>  4 words 
        //  HERE                                    |  |  | |  |  | | |  | |  |                  010    =>  8 words
        //   |                                      |  |  | |  |  | | |  | |  |                  011    => 16 words
        //   |                                      |  |  | |  |  | | |  | |  -> 0=Burst Wrap    100    => 32 words *CR1.5* ONLY
        //   |        Register Select  <-------------  |  | |  |  | | |  | |     1=No Wrap       111    => continuous (default)
        //   |        00=RCR                           |  | |  |  | | |  | |                     OTHERS => reserved 
        //   -------->[B][COLOR="#FF0000"]10=BCR[/COLOR][/B]                           |  | |  |  | | |  | -> *CR1.5* Drv Strngth
        //            01=DIDR *CR1.5* ONLY             |  | |  |  | | |  | |  00 = Full
        //                                             |  | |  |  | | |  | |  01 = 1/2 (default) ---> *CR1.0* Drv Strngth
        //                                             |  | |  |  | | |  | |  10 = 1/4           |    BCR[4]=reserved
        //              Reserved set to 00 <------------  | |  |  | | |  | |  11 = reserved      |    BCR[5]=0=Full
        //                                                | |  |  | | |  | -----------------------    BCR[5]=1=1/4
        //                     Operating Mode      <------- |  |  | | |  ->  Reserved set to 00's
        //                     [B][COLOR="#FF0000"]0=Synchronous burst[/COLOR][/B]          |  |  | | |      
        //                     1=Asynchronous (defalut)     |  |  | | ->  Wait  
        //                                                  |  |  | |     0 = Asserted during delay
        //                    Initial Latency      <---------  |  | |     1 = Asserted one data cycle before delay (default)
        //                    0=Variable(Default)              |  | |    
        //                    1=Fixed *CR1.5* ONLY             |  | ->  Reserved set to 0
        //                                                     |  |     
        //                                                     |  ---------------------------->  Wait Polarity
        //                                                     |                                 0=Active LOW
        //                                                     - >  Latency counter              1=Active HIGH (default)
        //                                                          010=Code 2
        //                                                          011=Code 3(default)
        //                                                          100=Code 4
        //                                                          101=Code 5
        //                                                          110=Code 6
        //                                                          else=Reserved
 

Hi,

Micron PSRAM/CellularRAM operation is well described in the official document:

**broken link removed**

Vadim
 

Found this thread as I was just going through the same exercise of getting the Nexys 3 to talk to the PSRAM in simple async mode. The verilog simulation code on the Micron site was invaluable- just added an instantiation of the psram model in the top level testbench for my code, and you get a nice working psram in the sim- it also nicely spits out info messages and timing warnings as the simulation accesses the memory. VERY helpful in nailing down the proper timing within the state machine.

I turned my code into a nice reusable module for future use. Also did the same for outputting the read-back data to the 7-seg display, something else I'm sure I'll be reusing. Happy to post code if someone else has a need for not reinventing the wheel. I may look into adding page mode async support to get a little more speed out of it, or maybe even whole hog synchronous burst mode...

Andy
 

Hello,
I want to use PSRAM for image capturing and storage for VGA display. The code Leep posted works for me, Thank You very much!! If I could get tested code from you Andrew I would appreciate it very much! :)

Jakub
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top