Would MiSTer ever be able to run a Neo Geo core?

https://github.com/MiSTer-devel/Main_MiSTer/wiki

Moderators: Mug UK, Zorro 2, Greenious, spiny, Sorgelig, Moderator Team

djsquare
Atariator
Atariator
Posts: 23
Joined: Thu Oct 18, 2018 2:01 am

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby djsquare » Fri Jan 18, 2019 10:43 pm

bitfan2011 wrote:support for Universe BIOS would be 1000000% appreciated :]

I agree 100% but isn't the current version under copyright? I think the previous versions are free for public use

User avatar
remowilliams
Captain Atari
Captain Atari
Posts: 233
Joined: Mon Apr 02, 2007 1:49 am

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby remowilliams » Wed Jan 23, 2019 7:49 pm

djsquare wrote:
bitfan2011 wrote:support for Universe BIOS would be 1000000% appreciated :]

I agree 100% but isn't the current version under copyright? I think the previous versions are free for public use


3.3 and older are currently available freely for personal use under terms.

http://unibios.free.fr/download.html

User avatar
SmokeMonster
Atari User
Atari User
Posts: 41
Joined: Wed Oct 03, 2018 2:26 pm

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby SmokeMonster » Wed Jan 23, 2019 9:39 pm

Support for Unibios would be awesome and it should just plug in exactly like the stock Neo Geo BIOS. But, most of its features (except cheats) could just be programmed in the menu too as an alternative. Unibios is second nature to those of us with Neo Geos though, and is very slick and polished. Feels like a native part of the MVS to me.

If Unibios is supported perfectly in the core, it will be worth it for users to buy a license and get the latest version too. Although as remo said, v3.3 and older are free.

bitfan2011
Obsessive compulsive Atari behavior
Obsessive compulsive Atari behavior
Posts: 108
Joined: Sat Dec 29, 2018 5:46 pm

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby bitfan2011 » Wed Jan 23, 2019 11:44 pm

SmokeMonster wrote:Support for Unibios would be awesome and it should just plug in exactly like the stock Neo Geo BIOS. But, most of its features (except cheats) could just be programmed in the menu too as an alternative. Unibios is second nature to those of us with Neo Geos though, and is very slick and polished. Feels like a native part of the MVS to me.

If Unibios is supported perfectly in the core, it will be worth it for users to buy a license and get the latest version too. Although as remo said, v3.3 and older are free.


yeah, unibios is usually my first stop when starting a game. i like to set the region, violence, difficulty to authentic settings. Shin Samurai Spirits, etc. have various violence and language options.

ReedSolomon
Atariator
Atariator
Posts: 27
Joined: Tue Oct 09, 2018 1:52 am

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby ReedSolomon » Fri Jan 25, 2019 4:35 pm

looks like furrtek has got a basic core running about 10 neo geo games on mister no sound for now and he's not sure if he'll need anything special going further but at least theres a start.

from his patreon (https://www.patreon.com/posts/24218461) page:

If you want to try it out, use my hacked-up MiSTer loader binary from here: https://github.com/furrtek/Main_MiSTer
Grab the core .rbf from here: https://github.com/furrtek/Neogeo_MiSTer/tree/master/output_files
And he XML romset file here: https://github.com/furrtek/Neogeo_MiSTer/blob/master/romsets.xml

Create a "neogeo" folder at the root of your MiSTer SD card, put romsets.xml in it along with the easily found "neo-epo.sp1" bios, and correctly named folders with ROMs. See the xml file for currently supported games (you can try adding some if you can figure out the format).

PhantombrainM
Captain Atari
Captain Atari
Posts: 168
Joined: Fri Mar 16, 2018 9:10 am

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby PhantombrainM » Fri Jan 25, 2019 7:51 pm

Interesting! I tried it. But it's not working.

After selecting the rom nothing happens. I can see in the videos of furrtek that it's loading the files and that it's showing up in the GUI on the bottom left. But at my setup nothing happens. Of course I put the .xml into the neogeo folder, i checked the rom names and even the file names and I put the one bios file into the root neogeo folder (I also tried the whole content).

Anyone tried it with success?
Two beer or not two beer? - Shakesbeer.

crocky
Atariator
Atariator
Posts: 21
Joined: Fri Nov 23, 2018 2:01 pm

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby crocky » Fri Jan 25, 2019 7:57 pm

PhantombrainM wrote:Interesting! I tried it. But it's not working.

After selecting the rom nothing happens. I can see in the videos of furrtek that it's loading the files and that it's showing up in the GUI on the bottom left. But at my setup nothing happens. Of course I put the .xml into the neogeo folder, i checked the rom names and even the file names and I put the one bios file into the root neogeo folder (I also tried the whole content).

Anyone tried it with success?



Did you change the main mister file as it said?

Bartdesign
Atarian
Atarian
Posts: 3
Joined: Fri Dec 07, 2018 12:12 am

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby Bartdesign » Fri Jan 25, 2019 7:58 pm

PhantombrainM wrote:Interesting! I tried it. But it's not working.

After selecting the rom nothing happens. I can see in the videos of furrtek that it's loading the files and that it's showing up in the GUI on the bottom left. But at my setup nothing happens. Of course I put the .xml into the neogeo folder, i checked the rom names and even the file names and I put the one bios file into the root neogeo folder (I also tried the whole content).

Anyone tried it with success?


I've also tried it, but i only see colored bars when i load the neo geo core. When i load the rom, you see the roms load in the bottom but it does not do anything after that. I've double checked everything, different bios files. Changed the main mister file. It still gives me the colored bars. But super impressive it is already working, never expected that to happen so soon. Congrats Furrtek!

User avatar
kitrinx
Captain Atari
Captain Atari
Posts: 152
Joined: Wed Sep 26, 2018 6:03 am

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby kitrinx » Fri Jan 25, 2019 7:58 pm

PhantombrainM wrote:Interesting! I tried it. But it's not working.

After selecting the rom nothing happens. I can see in the videos of furrtek that it's loading the files and that it's showing up in the GUI on the bottom left. But at my setup nothing happens. Of course I put the .xml into the neogeo folder, i checked the rom names and even the file names and I put the one bios file into the root neogeo folder (I also tried the whole content).

Anyone tried it with success?


It's a little finicky to get working, and right now analog only. neo-epo.sp1 is just a renamed neo-epo.rom, and the romset is standard mame, though sometimes you have to rename .bin to the correct extension (.c1, etc). Don't forget to restart after updating to his custom main binary. It seems to at full speed, very promising.

PhantombrainM
Captain Atari
Captain Atari
Posts: 168
Joined: Fri Mar 16, 2018 9:10 am

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby PhantombrainM » Fri Jan 25, 2019 8:06 pm

crocky wrote:
Did you change the main mister file as it said?


No, but I did a MD5 checksum on the original one and it's the same as furrtek's ;)

kitrinx wrote:
It's a little finicky to get working, and right now analog only. neo-epo.sp1 is just a renamed neo-epo.rom, and the romset is standard mame, though sometimes you have to rename .bin to the correct extension (.c1, etc). Don't forget to restart after updating to his custom main binary. It seems to at full speed, very promising.


Ok, I searched a while for a rom which had a neo-epo.sp1 file in it. I try to rename another one to your suggestions. And yeah, I am on analog because I saw it's a light Version and the color bars on HDMI ;)

P.S.: It was the MISTer binary! I went from the furrtek repository into the releases folder but it was a forward to the official ones from sorgelig? I had to click on the mister text file icon below and went into a subfolder of furrteks archive...there was only one mister binary, 4 hours old and now its working. Thank you all!
Two beer or not two beer? - Shakesbeer.

flain
Atariator
Atariator
Posts: 22
Joined: Sat Nov 03, 2018 6:21 am

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby flain » Sun Jan 27, 2019 12:04 pm

Unibios works on the core https://twitter.com/SmokeMonsterTWI/sta ... 7185968128

It's very impressive just how accurate this seems to be in such a short amount of time!

User avatar
furrtek
Retro freak
Retro freak
Posts: 11
Joined: Tue May 22, 2018 11:57 pm
Contact:

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby furrtek » Fri Feb 01, 2019 5:59 pm

Hello~

I'm currently trying out the DDR3 memory to store the graphics data, which make up most of a game.
The NeoGeo's hardware limit for sprite graphics is 128MiB, so the 256MiB currently allocated in the DDR3 for the FPGA would be perfect for that.

Unsurprisingly it works most of the time so the games are playable, but the periodic latency hiccups cause very noticeable and annoying glitches. I'm clearly seeing that happening in Signaltap, when the "data ready" signal takes longer than what the NeoGeo allows to get the data after providing the address.

I had already optimized the loading of graphics data to do 4-word burst reads on the SDRAM, but that doesn't change the fact that the NeoGeo's video chip requires valid data before 250ns and that causes problems with the DDR3. Above those 4 sequential word reads, the address is random.
There's no way to tell the video chip to wait as it could be done on program data with /DTACK. Of course its logic could be modified to allow that, but that would mean less time to parse sprites, so less sprites per line, deviation from the original logic, and everything would go to the shitter...

If I understood it right, there's no point in increasing the DDR clock frequency on the FPGA side ? The controller still runs at the frequency set up by the HPS ?

Feeling like the DDR3 really can't be used for graphics in this case, even with 250ns of slack. If that's the case, then it could be used for program data (and maybe audio samples) but then the 32MiB of SDRAM will still not be enough to run all games...

Tripmonkeyuk
Atarian
Atarian
Posts: 8
Joined: Thu Dec 20, 2018 6:33 am

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby Tripmonkeyuk » Fri Feb 01, 2019 6:27 pm

I remember that Pepone had similar problems with getting NeoGeo to run on the GP2X handheld years ago due to the limited amount of RAM. I can't remember exactly how he got around it, but it might be worth looking into??

I found an old interview with him that mentions it..
"Pepone: It works the same as the normal dumpgfx. The dumpgfx tool load all sprite data in memory, decrypt them, and write the result in a gfx file. It can't be done directly on the GP2X though, because it needs a lot of memory (often more than 128MB".

Good luck at getting NeoGeo working :)

Dubon
Retro freak
Retro freak
Posts: 10
Joined: Fri Oct 26, 2018 4:01 am

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby Dubon » Fri Feb 01, 2019 7:11 pm

Hi Furrtek,

Personally i wouldnt mind investing in a larger size ddr module, but iirc Sorgelig said, in a different thread that the larger sdram chips wouldnt be able to run at the same clockspeeds the current module does. Theres also the matter of number of pins required versus the current IO expansion board.
Maybe Sorgelig could provide some input

Sorgelig
Fuji Shaped Bastard
Fuji Shaped Bastard
Posts: 4637
Joined: Mon Dec 14, 2015 10:51 am
Location: Russia/Taiwan

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby Sorgelig » Fri Feb 01, 2019 7:29 pm

There is no way to increase the clock of DDR3. What clock do you use for AvalonMM interface now?
Graphics usually has some predictable access, so may be it's possible to predict which address will be accessed and prefetch that data? May be cache will help?
Tiles, sprites - they are usually larger than 4 words. Sure they split to raster lines, but probably it's possible to predict the same sprite will be drawn on the next line too, so can cache it's data for next lines.

For SDRAM modules. Maximum size of TSOP54 RAM is 64MB. One such chip i could make work at 130MHz max. Two such chips in parallel will probably lower the max clock to around 80MHz.
There are probably some more choice in BGA chips. But such modules will be harder to manufacture and it won't be DIY friendly.

User avatar
furrtek
Retro freak
Retro freak
Posts: 11
Joined: Tue May 22, 2018 11:57 pm
Contact:

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby furrtek » Fri Feb 01, 2019 7:59 pm

Thanks for the input !

Tripmonkeyuk wrote:I remember that Pepone had similar problems with getting NeoGeo to run on the GP2X handheld years ago due to the limited amount of RAM. I can't remember exactly how he got around it, but it might be worth looking into??

His tool mixed up the graphics roms into a single file that could be read directly from the SD card. From the README:
Gngeo 0.6.7 add Big roms support on the gp2x. The principe is simply to mmap a special dump file and use
it as gfx memory. This file have to be created on your box before.
I'm pleasently surprised by the result. On my modest SD, games like rbff1 are playable. A fast SD is still
recomended

I'm also surprised that streaming from the SD card is fast enough, but it's software emulation so the time it takes and how the sprites are rendered doesn't matter much, as long as the whole frame is rendered in time.

Sorgelig wrote:There is no way to increase the clock of DDR3. What clock do you use for AvalonMM interface now?

I'm actually not sure. Wired sys_clk (currently 96MHz) to DDRAM_CLK, if that's related in any way.

Sorgelig wrote:Graphics usually has some predictable access, so may be it's possible to predict which address will be accessed and prefetch that data? May be cache will help?
Tiles, sprites - they are usually larger than 4 words. Sure they split to raster lines, but probably it's possible to predict the same sprite will be drawn on the next line too, so can cache it's data for next lines.

I've also thought of caching whole tiles (64 words instead of 4 for just one line), but that doesn't prevent a long access delay from happening, so the first word (top left corner of the tile) might still not be read in time.

Sorgelig wrote:For SDRAM modules. Maximum size of TSOP54 RAM is 64MB. One such chip i could make work at 130MHz max. Two such chips in parallel will probably lower the max clock to around 80MHz.
There are probably some more choice in BGA chips. But such modules will be harder to manufacture and it won't be DIY friendly.

Was worried about capacitive loading... 80MHz might be enough but the board wouldn't work with all the other cores :/

Heh, the Neo CD might actually be easier to implement since it can only load 7MiB at a time :p

BeoWolff
Atarian
Atarian
Posts: 3
Joined: Tue Jan 22, 2019 11:15 pm

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby BeoWolff » Sat Feb 02, 2019 2:10 am

Furrtek, can you give us the option to run the NeoGeo core at 120mhz or even 144mhz, even if it potentially does eventually destroy our SDRAM boards? My SDRAM board is stable at 150mhz. I wouldn't mind the option to do that, and then replace it with a newly designed 2 SDRAM board when if it stops working.

Sorgelig
Fuji Shaped Bastard
Fuji Shaped Bastard
Posts: 4637
Joined: Mon Dec 14, 2015 10:51 am
Location: Russia/Taiwan

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby Sorgelig » Sat Feb 02, 2019 3:53 am

furrtek wrote:Wired sys_clk (currently 96MHz) to DDRAM_CLK, if that's related in any way.

You can experiment with higher clocks, but i think it won't make difference.

furrtek wrote:I've also thought of caching whole tiles (64 words instead of 4 for just one line), but that doesn't prevent a long access delay from happening, so the first word (top left corner of the tile) might still not be read in time.

i don't know NeoGeo, but many other consoles have sprites draw one line in advance. How about draw the line into 1-line buffer in advance, so probably you won't even need to change the logic from current one and don't cache the whole sprites. Just draw one line in advance with DDR3 pace into the line buffer. And on the next line you should have the whole line to draw on the final frame.

furrtek wrote:Heh, the Neo CD might actually be easier to implement since it can only load 7MiB at a time :p

As Wikipedia says most games released on CD as well. So it can be definitely a viable alternative. If it doesn't need more than 7MB or RAM then SDRAM can be used.
Probably some games unreleased on CD aren't big and can fit to 32MB of SDRAM. Then you will cover the most games.

I see on different sites that most games are below 256MBits. Many larger games have CD versions. Probably if subtract non-graphics parts more games will fit. So may be start to support <256MBit games first and then later can think what can be improved to move to DDR3.

bitfan2011
Obsessive compulsive Atari behavior
Obsessive compulsive Atari behavior
Posts: 108
Joined: Sat Dec 29, 2018 5:46 pm

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby bitfan2011 » Sat Feb 02, 2019 8:47 am

furrtek wrote:Heh, the Neo CD might actually be easier to implement since it can only load 7MiB at a time :p


Super cool idea, the CD titles are more obscure due to the insane loadtimes compared to the carts. But each format has some interesting aspects to explore. The massive later cart-based games always seem to be the hardest part about any sort of NG "emulation". Just seeing NG software running with such clean A/V would (will...) be awesome.

PhantombrainM
Captain Atari
Captain Atari
Posts: 168
Joined: Fri Mar 16, 2018 9:10 am

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby PhantombrainM » Sun Feb 03, 2019 10:16 am

No one liked the CD games back in the day because of the very long loading times during gameplay ;)

What if you split the rom files (graphic /sprite data) into chunks?
Put every let's say first 1MB / 512KB chunks into the 32MB RAM and when it's called it can load the rest from DDR3 in parallel?
Two beer or not two beer? - Shakesbeer.

breiztiger
Obsessive compulsive Atari behavior
Obsessive compulsive Atari behavior
Posts: 137
Joined: Sun Sep 20, 2009 6:54 am
Location: FRANCE

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby breiztiger » Tue Feb 05, 2019 5:15 pm

@sorgelig

does the lastest version of MiSTer (main core) from today have special rom loading scheme from the one for neogeo core ?

Sorgelig
Fuji Shaped Bastard
Fuji Shaped Bastard
Posts: 4637
Joined: Mon Dec 14, 2015 10:51 am
Location: Russia/Taiwan

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby Sorgelig » Tue Feb 05, 2019 5:48 pm

breiztiger wrote:@sorgelig

does the lastest version of MiSTer (main core) from today have special rom loading scheme from the one for neogeo core ?


No. It's not a problem to add it when it will be required.

User avatar
furrtek
Retro freak
Retro freak
Posts: 11
Joined: Tue May 22, 2018 11:57 pm
Contact:

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby furrtek » Thu Feb 07, 2019 6:35 pm

PhantombrainM wrote:What if you split the rom files (graphic /sprite data) into chunks?
Put every let's say first 1MB / 512KB chunks into the 32MB RAM and when it's called it can load the rest from DDR3 in parallel?


Usage of graphics isn't segmented like that, nothing prevents a game from using tile #17 and tile #65392 at the same time.
CD games had re-organized graphics so that stages/rounds could have all their graphics fit in RAM between loadings.

Maybe I'm misunderstanding. If you thought of using the SDRAM to compensate for the DDR3 burst start-up time, then @ghogan42 on Twitter had the same idea. But giving it some thought I'm not sure it could work:

As I wrote, four 16-bit words must be read for a given 16 pixel sprite line. Since the data is in a planar format, the 8 leftmost pixels become corrupt when the DDR3 isn't fast enough (because each word is half of the bitplanes for 8 pixels).
So the left part of each tile could be stored in SDRAM for fast access and the right part in DDR3, which would have more time to reply.
BUT #1: Tiles can be flipped, so the first 8 pixels to draw can be on either side. It can't be known until rendering starts.
BUT #2: Halving 128MiB would still not make it fit in the SDRAM module.

So how about doing that on whole tiles instead of each line ? There can only be max. 96 tiles per line so a tile cache wouldn't be that big. The SDRAM could store the topmost line of each tile, and the rest would be loaded in cache from DDR3 just once.
That would only require 8MiB of SDRAM (and 120MiB of DDR3).
BUT: The NeoGeo can do vertical shrinking, it's done by simply skipping tile lines during rendering. If a sprite is shrunk down to the point each of its tiles is one-line high, that means a new tile has to be loaded each line, defeating the whole purpose of the cache !

Sorgelig wrote:i don't know NeoGeo, but many other consoles have sprites draw one line in advance. How about draw the line into 1-line buffer in advance, so probably you won't even need to change the logic from current one and don't cache the whole sprites. Just draw one line in advance with DDR3 pace into the line buffer. And on the next line you should have the whole line to draw on the final frame.


The NeoGeo does that. Actually it Y-matches the sprites at y-2 and renders them at y-1 so they can be shifted out at y.
That's what I meant by keeping the original logic: there's only enough time to render max 96 sprite lines, if rendering is slowed down then less sprites can be drawn. I can't see how that could be done without compromises like having to add line buffers and/or changing the original logic.

User avatar
Alynna
Atari freak
Atari freak
Posts: 57
Joined: Tue Sep 18, 2018 5:54 pm

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby Alynna » Fri Feb 08, 2019 12:28 am

This is not working yet, but I am confident I will get it working, but I wanted to share where I am going with DDR3 access.
This is the code (so far) for my BRAM cached DDR3 controller.
The current code uses 128K of BRAM for cache in 1KB pages.
It commits and reads 1K pages in 128 byte bursts (or will when it works)..

I am still new and I have needed a break from living my life between 15 minute compiles (I've been doing this for the Amiga core).
Still I think that this code might be helpful for the Neo Geo core, or at least give a baseline idea of where to go with it.

Code: Select all

// bddram.v
// Copyright (c) 2019 Alynna
//
//
// This source file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// ------------------------------------------
//

module single_port_ram
#(
parameter DATA_WIDTH=64,
parameter ADDR_WIDTH=7,
parameter PAGE_WIDTH=7)
(
   input [(DATA_WIDTH-1):0] data,
   input [(ADDR_WIDTH-1):0] addr,
   input [(PAGE_WIDTH-1):0] page,
   input we, clk,
   output [(DATA_WIDTH-1):0] q
);

   // Declare the RAM variable
   reg [DATA_WIDTH-1:0] ram[(2**PAGE_WIDTH)-1:0][(2**ADDR_WIDTH)-1:0];

   // Variable to hold the registered read address
   reg [ADDR_WIDTH-1:0] addr_reg;
   reg [PAGE_WIDTH-1:0] page_reg;

   always @ (posedge clk)
   begin
      // Write
      if (we)
         ram[page][addr] <= data;
      page_reg <= page;
      addr_reg <= addr;
   end

   // Continuous assignment implies read returns NEW data.
   // This is the natural behavior of the TriMatrix memory
   // blocks in Single Port mode. 
   assign q = ram[page_reg][addr_reg];
endmodule
module ddram #(
   parameter MODE = 0
   // MODE=0: 512M addressing space, risk to ASCAL (avoid using lowest 32M memory)
   // MODE=1: 256M addressing space at top of HPS RAM, use freely
)
(
   input         DDRAM_CLK,
   inout  [7:0]  debug,
   
   input         DDRAM_BUSY,       // waitrequest
   output [7:0]  DDRAM_BURSTCNT,    // burstcount
   output [28:0] DDRAM_ADDR,       // Exact HPS memory address
   input  [63:0] DDRAM_DOUT,       // readdata
   input         DDRAM_DOUT_READY, // readdatavalid
   output        DDRAM_RD,       // read
   output [63:0] DDRAM_DIN,       // writedata
   output [7:0]  DDRAM_BE,       // byteenable
   output        DDRAM_WE,       // write

   inout [1:0]   cfg_req,
   
   input [31:0]  wr_a,
   input [15:0]  wr_d,
   input [7:0]   wr_d8,
   input [1:0]   wr_be,
   input         wr_req,
   output        wr_ack,

   input             bus16,
   output [7:0]  page,
   
   input  [31:0] rd_a,
   output [15:0] rd_d,
   output [7:0]  rd_d8,
   input         rd_req,
   output        rd_ack
);

localparam PAGES = 64;   // I had to hardcode this..

// Structure
reg PAGE_SWAP [128-1:0];          // 1: Permitted to be swapped out
reg [15:0] PAGE_AGE [128-1:0];      // Page ages
reg PAGE_W [128-1:0];               // Set if a page has been written to
reg [28:0] PAGE_ADDR [128-1:0];      // Page addresses in core - 1K boundaries

wire BRAM_CLK;

// Inferred BRAM
single_port_ram #(
 .DATA_WIDTH(64),                     // Size of data bus, 64 is right
 .ADDR_WIDTH(7),                     // Size of burst length.  128 for now
 .PAGE_WIDTH(7)                     // 2**x = Number of pages (6=64)
) bram (
 .data(BIN),
 .addr(BADDR),
 .page(BPAGE),
 .we(BWE),
 .q(BOUT),
 .clk(BRAM_CLK)
);

wire [63:0]    BIN, BOUT;
wire [7:0]       BPAGE;
wire [6:0]     BADDR;
wire             BWE;

reg [7:0]     ram_burst;
reg [63:0]    ram_data;
reg [28:0]    ram_address;
reg           ram_read = 0;
reg           ram_write = 0;

reg rd_acki,wr_acki;
assign DDRAM_ADDR        = MODE ? {1'b0,ram_address[27:10],10'b0} : {ram_address[28:10],10'b0};
assign DDRAM_BE          = 8'b11111111;
assign DDRAM_BURSTCNT    = ram_burst;
assign DDRAM_DIN         = ram_data;
assign DDRAM_RD          = ram_read;
assign DDRAM_WE          = ram_write;
assign rd_ack            = rd_acki;
assign wr_ack            = wr_acki;

reg [31:0] rd_addr, wr_addr;
reg [15:0] rd_dat16, wr_dat16;
reg [7:0] rd_dat8, wr_dat8;
reg [1:0] wr_byteen;
reg [7:0] rd_pend;
reg [7:0] wr_pend;
reg [7:0] sw_page; // Page being swapped
reg [1:0] sw_pend;
reg [1:0] phase;

assign rd_d = rd_dat16;
assign rd_d8 = rd_dat8;

reg [7:0] x8, y8;
reg [15:0] x16, y16;
reg [7:0] signal;
// 00: No errors
// Bit 7: Any other errors
// Bit 0: Page not found
// 7: 1: Write / 0: Read
// 6-5: phase;

task age_pages;
reg [7:0] p;
begin
   for (p=0; p<128; p++) PAGE_AGE[p] <= (PAGE_AGE[p] == 16'hFFFF) ? 16'hFFFF : (PAGE_AGE[p] + 16'd1);
end endtask

task find_page_old;
// Find the oldest page
reg [7:0] p;
reg [15:0] r;
begin
   r <= 16'd0;
   for (p=127; p[7]; p--) begin
      if ((PAGE_SWAP[p]==1'b1) && (PAGE_AGE[p] > r)) begin
         r <= PAGE_AGE[p];
         page <= p;
      end
   end
end endtask

task find_page_a(reg [28:0] addr);
// Find page matching the given address
reg [7:0] p;
begin
   signal[0] <= 1'b1;
   for (p=127; p[7]; p--) begin
      if (PAGE_ADDR[p][28:10]==addr[28:10]) begin
         signal[0]    <= 1'b0;
         page          <= p;
      end
   end
end endtask

task get16(reg [28:0] addr);
// Get a 16 bit value if available in cache
begin
   find_page_a(addr);
   if (!signal[0]) begin
      BWE <= 0;
      BPAGE <= page;
      BADDR <= addr[9:3];
      rd_dat16 <= BOUT[(addr[2:1] << 4) +:16];
      PAGE_AGE[page] <= 16'b0;
      rd_pend <= 8'hff;
      BRAM_CLK <= 1'b1;
      phase <= 2'b11;
   end
end endtask

task get8(reg [28:0] addr);
// Get a 8 bit value if available in cache
begin
   find_page_a(addr);
   if (!signal[0]) begin
      BWE <= 0;
      BPAGE <= page;
      BADDR <= addr[9:3];
      rd_dat8 <= BOUT[(addr[2:0] << 3) +:8];
      PAGE_AGE[page] <= 16'b0;
      rd_pend <= 8'hff;
      BRAM_CLK <= 1'b1;
      phase <= 2'b11;
   end
end endtask

task put16(reg [28:0] addr, reg [15:0] val);
// Put a 16 bit value if its page is cached.
begin
   find_page_a(addr);
   if (!signal[0]) begin
      BPAGE <= page;
      BADDR <= addr[9:3];
      BIN[(addr[2:1] << 4) +:16] <= val;
      BWE <= 1;
      PAGE_AGE[page] <= 16'b0;
      PAGE_W[page] <= 1'b1;
      wr_pend <= 8'hff;
      BRAM_CLK <= 1'b1;
      phase <= 2'b11;
   end else signal[0] <= 1'b1;
end endtask

task put8(reg [28:0] addr, reg [7:0] val);
// Put an 8 bit value if its page is cached.
begin
   find_page_a(addr);
   if (!signal[0]) begin
      BPAGE <= page;
      BADDR <= addr[9:3];
      BIN[(addr[2:1] << 3) +:8] <= val;
      BWE <= 1;
      PAGE_W[page] <= 1'b1;
      PAGE_AGE[page] <= 16'b0;
      wr_pend <= 8'hff;
      BRAM_CLK <= 1'b1;
      phase <= 2'b11;
   end else signal[0] <= 1'b1;
end endtask

task write_page_s(reg [6:0] p);
// Begin process of sending a cache page to ddr3
begin
   page         <= p;
   BWE         <= 0;
   BPAGE       <= p;
   BADDR       <= 0;
   ram_address <= PAGE_ADDR[page];
   ram_burst   <= 8'd128;
   wr_pend     <= 8'd127;
   BRAM_CLK      <= 1;
   wr_acki       <= 0;
   ram_write   <= 0;
end endtask

task write_page_l(reg [6:0] p);
// Continue write burst
begin
   if (!DDRAM_BUSY && !wr_pend[7]) begin
      BADDR       <= ~wr_pend[6:0];
      ram_data    <= BOUT;
      BRAM_CLK      <= 1'b0;
      ram_write   <= 1'b1;
   end
end endtask

task write_page_h(reg [6:0] p);
// Continue write burst
begin
   if (!DDRAM_BUSY && !wr_pend[7]) begin
      wr_pend      <= wr_pend - 8'd1;
      BRAM_CLK      <= 1'b1;
      ram_write   <= 1'b0;
   end
end endtask

task reset_page(reg [6:0] p, reg [15:0] addr, reg ptype=1'b1);
// Reset the page and set a new address for it.
begin
   page               <= p;
   PAGE_ADDR[page]   <= addr;
   PAGE_AGE[page]      <= 16'b0;
   PAGE_SWAP[page]   <= ptype;
   PAGE_W[page]      <= 1'b0;
end endtask

task read_page_s(reg [6:0] p);
// Begin process of getting a cache page from ddr3
begin
   page         <= p;
   BWE         <= 1;
   BPAGE       <= p;
   BADDR       <= 0;
   ram_burst   <= 8'd128;
   rd_pend     <= 8'd127;
   BRAM_CLK      <= 1'b0;
   ram_read      <= 1'b1;
   rd_acki       <= 0;
end endtask

task read_page_l(reg [6:0] p);
// Continue read burst
begin
   if (!DDRAM_BUSY && DDRAM_DOUT_READY && !rd_pend[7]) begin
      BADDR       <= ~rd_pend[6:0];
      BIN          <= DDRAM_DOUT;
      BRAM_CLK      <= 1'b1;
      ram_read      <= 1'b0;
   end
end endtask

task read_page_h(reg [6:0] p);
// Continue read burst
begin
   if (!DDRAM_BUSY && DDRAM_DOUT_READY && !rd_pend[7]) begin
      rd_pend       <= rd_pend - 8'd1;
      BRAM_CLK      <= 1'b0;
      ram_read      <= 1'b1;
   end
end endtask

task page_sw_s;
// Begin process of swapping a page out and bringing a new one in.
// Page swapped out will be the least accessed page.
reg [7:0] p;
begin
   // We only have to write the page if its been updated
   find_page_old;
   sw_pend <= (PAGE_W[page] == 1'b1) ? 2'b01 : 2'b10;
   sw_page <= page;
end endtask

// CFG interface:
// Use cfg_req:1 with rd_req:0 and wr_req:0 for the following tasks:
// cfg_req:1 wr_d:16'd0 wr_d8:<page>: Make a page unswappable
// cfg_req:1 wr_d:16'd1 wr_d8:<page>: Make a page swappable
// cfg_req:1 wr_d:16'd2               Look up a page by address (returned in rd_d8)
// cfg_req:1 wr_d:16'd3 wr_d8:<page> wr_a:<addr> Assign an address to a page
// cfg_req:1 wr_d:16'd4 wr_d8:<page> Make a page young (age = 0)
// cfg_req:1 wr_d:16'd5 wr_d8:<page> Make a page old (age = 65535)
// cfg_req:1 wr_d:16'd7 wr_d8:<page> wr_a:<addr> Reset a page (does not commit it!)
   
task cfg(reg [28:0] a, reg [15:0] f, reg [6:0] p);
begin
   debug <= 8'b11100000;
   case (f)
      16'd0: PAGE_SWAP[p] <= 1'b0;
      16'd1: PAGE_SWAP[p] <= 1'b1;
      16'd2: begin
         find_page_a(a);
         rd_dat8 <= page;
         end
      16'd3: PAGE_ADDR[p] <= a;
      16'd4: PAGE_AGE[p] <= 16'h0;
      16'd5: PAGE_AGE[p] <= 16'hFFFF;
      16'd7: reset_page(p, a, 1'b1);
      default: ;
   endcase      
end endtask

// Phases:
// 00: Ready for r/w request
// 01: Initialize page swap
// 02: Continue page swap
// 03: Finalize transfer (wait for rd/wr req to go low

reg [7:0] pg = 0;
reg init = 0;
always @(posedge DDRAM_CLK) begin
   if (!init) begin
      for (pg=0; pg < 128; pg++) begin // Cheap ass init block
         PAGE_ADDR[pg] <= 28'b0;
         PAGE_AGE[pg] <= 16'b0;
         PAGE_W[pg] <= 1'b0;
         PAGE_SWAP[pg] <= 1'b1;
      end
      rd_pend <= 8'hFF;
      wr_pend <= 8'hFF;
      phase <= 2'b00;
      init <= 1;
   end
   debug[6:5] <= phase;
   age_pages;
   if (phase == 2'b00) begin // Should be in phase 0 whenever we are ready to process requests.
      if (wr_req) begin
         debug[7] <= 1'b1;
         if (bus16)
            case (wr_be)
               2'b00: ;
               2'b01: put8(wr_a,wr_d[7:0]);
               2'b10: put8(wr_a+1,wr_d[15:8]);
               2'b11: put16(wr_a,wr_d);
            endcase
         else
            put8(wr_a, wr_d8);
         if (signal[0]) begin // Page not found
            page_sw_s;
            sw_page <= page;
            debug[4:0] <= {3'b001,sw_pend};
            case (sw_pend)
               2'b01: write_page_s(sw_page);
               2'b10: read_page_s(sw_page);
               default: ;
            endcase         
            phase <= 2'b01;
            debug[4:0] <= {3'b001,sw_pend};
         end else begin
            phase <= 2'b11;
         end;
      end else if (rd_req) begin
         debug[7] <= 1'b0;
         if (bus16)    get16(rd_a);
         else          get8(rd_a);
         if (signal[0]) begin
            page_sw_s;
            sw_page <= page;
            case (sw_pend)
               2'b01: write_page_s(sw_page);
               2'b10: read_page_s(sw_page);
               default: ;
            endcase         
            phase <= 2'b01;
            debug[4:0] <= {3'b001,sw_pend};
         end else phase <= 2'b11;   
      end else if (!wr_req && !rd_req && cfg_req) begin
         debug <= 8'b11100111;
         cfg(wr_a, wr_d, wr_d8);
         cfg_req <= 1'b0;
         phase <= 2'b10;
      end
   end else if (phase == 2'b01) begin // Phase 1: Swap page
      case (sw_pend)
         2'b01: write_page_l(sw_page);
         2'b10: begin // Reset page and set address
            if (rd_req)
               reset_page(sw_page,rd_a);
            else
               reset_page(sw_page,wr_a);            
         end
         2'b11: begin // Read page from DDR3
            read_page_l(sw_page);
         end
         default: ;
      endcase
      phase <= 2'b10;
      debug <= {6'b101001,sw_pend};
   end else if (phase == 2'b10 || (!rd_req && !wr_req)) begin // Continue or end decision
      case (sw_pend)
         2'b01: begin // Commit page to DDR3 (Skipped if no writes to the page)
            write_page_h(sw_page);
            if (wr_pend[7]) sw_pend <= 2'b10;
         end
         2'b10: begin // Reset page and set address
            read_page_s(sw_page);
            sw_pend <= 2'b11;
         end
         2'b11: begin // Read page from DDR3
            read_page_h(sw_page);
            if (rd_pend[7]) sw_pend <= 2'b00;
         end
         default: ;
      endcase
      BRAM_CLK <= 1'b0;
      if (rd_pend != 8'hFF || wr_pend != 8'hFF) begin
         phase <= 2'b01; // DDR3 burst continues
      end else begin   
         phase <= 2'b00; // Retry RW
      end
      
   end else if (phase == 2'b11 || (!rd_req && !wr_req)) begin
      // Wait for the core to be ready
      // We also detect if theres no request being made
      // to allow the phase to be reset to 0.
      signal <= 8'b00000000;
      BWE <= 1'b0;
      BRAM_CLK <= 1'b0;
      if (rd_pend==8'hFF)   rd_acki <= 1;
      if (wr_pend==8'hFF)   wr_acki <= 1;
   end
   if (!wr_req)         wr_acki <= 0;
   if (!rd_req)         rd_acki <= 0;
   if (!wr_req && !rd_req) phase <= 2'b00;
end
endmodule

User avatar
Alynna
Atari freak
Atari freak
Posts: 57
Joined: Tue Sep 18, 2018 5:54 pm

Re: Would MiSTer ever be able to run a Neo Geo core?

Postby Alynna » Fri Feb 08, 2019 2:19 pm

Alynna wrote:This is not working yet, but I am confident I will get it working, but I wanted to share where I am going with DDR3 access.
This is the code (so far) for my BRAM cached DDR3 controller.
The current code uses 128K of BRAM for cache in 1KB pages.
It commits and reads 1K pages in 128 byte bursts (or will when it works)..

I am still new and I have needed a break from living my life between 15 minute compiles (I've been doing this for the Amiga core).
Still I think that this code might be helpful for the Neo Geo core, or at least give a baseline idea of where to go with it.

Code: Select all

// bddram.v
// Copyright (c) 2019 Alynna
//
//
// This source file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// ------------------------------------------
//

module single_port_ram
#(
parameter DATA_WIDTH=64,
parameter ADDR_WIDTH=7,
parameter PAGE_WIDTH=7)
(
   input [(DATA_WIDTH-1):0] data,
   input [(ADDR_WIDTH-1):0] addr,
   input [(PAGE_WIDTH-1):0] page,
   input we, clk,
   output [(DATA_WIDTH-1):0] q
);

   // Declare the RAM variable
   reg [DATA_WIDTH-1:0] ram[(2**PAGE_WIDTH)-1:0][(2**ADDR_WIDTH)-1:0];

   // Variable to hold the registered read address
   reg [ADDR_WIDTH-1:0] addr_reg;
   reg [PAGE_WIDTH-1:0] page_reg;

   always @ (posedge clk)
   begin
      // Write
      if (we)
         ram[page][addr] <= data;
      page_reg <= page;
      addr_reg <= addr;
   end

   // Continuous assignment implies read returns NEW data.
   // This is the natural behavior of the TriMatrix memory
   // blocks in Single Port mode. 
   assign q = ram[page_reg][addr_reg];
endmodule
module ddram #(
   parameter MODE = 0
   // MODE=0: 512M addressing space, risk to ASCAL (avoid using lowest 32M memory)
   // MODE=1: 256M addressing space at top of HPS RAM, use freely
)
(
   input         DDRAM_CLK,
   inout  [7:0]  debug,
   
   input         DDRAM_BUSY,       // waitrequest
   output [7:0]  DDRAM_BURSTCNT,    // burstcount
   output [28:0] DDRAM_ADDR,       // Exact HPS memory address
   input  [63:0] DDRAM_DOUT,       // readdata
   input         DDRAM_DOUT_READY, // readdatavalid
   output        DDRAM_RD,       // read
   output [63:0] DDRAM_DIN,       // writedata
   output [7:0]  DDRAM_BE,       // byteenable
   output        DDRAM_WE,       // write

   inout [1:0]   cfg_req,
   
   input [31:0]  wr_a,
   input [15:0]  wr_d,
   input [7:0]   wr_d8,
   input [1:0]   wr_be,
   input         wr_req,
   output        wr_ack,

   input             bus16,
   output [7:0]  page,
   
   input  [31:0] rd_a,
   output [15:0] rd_d,
   output [7:0]  rd_d8,
   input         rd_req,
   output        rd_ack
);

localparam PAGES = 64;   // I had to hardcode this..

// Structure
reg PAGE_SWAP [128-1:0];          // 1: Permitted to be swapped out
reg [15:0] PAGE_AGE [128-1:0];      // Page ages
reg PAGE_W [128-1:0];               // Set if a page has been written to
reg [28:0] PAGE_ADDR [128-1:0];      // Page addresses in core - 1K boundaries

wire BRAM_CLK;

// Inferred BRAM
single_port_ram #(
 .DATA_WIDTH(64),                     // Size of data bus, 64 is right
 .ADDR_WIDTH(7),                     // Size of burst length.  128 for now
 .PAGE_WIDTH(7)                     // 2**x = Number of pages (6=64)
) bram (
 .data(BIN),
 .addr(BADDR),
 .page(BPAGE),
 .we(BWE),
 .q(BOUT),
 .clk(BRAM_CLK)
);

wire [63:0]    BIN, BOUT;
wire [7:0]       BPAGE;
wire [6:0]     BADDR;
wire             BWE;

reg [7:0]     ram_burst;
reg [63:0]    ram_data;
reg [28:0]    ram_address;
reg           ram_read = 0;
reg           ram_write = 0;

reg rd_acki,wr_acki;
assign DDRAM_ADDR        = MODE ? {1'b0,ram_address[27:10],10'b0} : {ram_address[28:10],10'b0};
assign DDRAM_BE          = 8'b11111111;
assign DDRAM_BURSTCNT    = ram_burst;
assign DDRAM_DIN         = ram_data;
assign DDRAM_RD          = ram_read;
assign DDRAM_WE          = ram_write;
assign rd_ack            = rd_acki;
assign wr_ack            = wr_acki;

reg [31:0] rd_addr, wr_addr;
reg [15:0] rd_dat16, wr_dat16;
reg [7:0] rd_dat8, wr_dat8;
reg [1:0] wr_byteen;
reg [7:0] rd_pend;
reg [7:0] wr_pend;
reg [7:0] sw_page; // Page being swapped
reg [1:0] sw_pend;
reg [1:0] phase;

assign rd_d = rd_dat16;
assign rd_d8 = rd_dat8;

reg [7:0] x8, y8;
reg [15:0] x16, y16;
reg [7:0] signal;
// 00: No errors
// Bit 7: Any other errors
// Bit 0: Page not found
// 7: 1: Write / 0: Read
// 6-5: phase;

task age_pages;
reg [7:0] p;
begin
   for (p=0; p<128; p++) PAGE_AGE[p] <= (PAGE_AGE[p] == 16'hFFFF) ? 16'hFFFF : (PAGE_AGE[p] + 16'd1);
end endtask

task find_page_old;
// Find the oldest page
reg [7:0] p;
reg [15:0] r;
begin
   r <= 16'd0;
   for (p=127; p[7]; p--) begin
      if ((PAGE_SWAP[p]==1'b1) && (PAGE_AGE[p] > r)) begin
         r <= PAGE_AGE[p];
         page <= p;
      end
   end
end endtask

task find_page_a(reg [28:0] addr);
// Find page matching the given address
reg [7:0] p;
begin
   signal[0] <= 1'b1;
   for (p=127; p[7]; p--) begin
      if (PAGE_ADDR[p][28:10]==addr[28:10]) begin
         signal[0]    <= 1'b0;
         page          <= p;
      end
   end
end endtask

task get16(reg [28:0] addr);
// Get a 16 bit value if available in cache
begin
   find_page_a(addr);
   if (!signal[0]) begin
      BWE <= 0;
      BPAGE <= page;
      BADDR <= addr[9:3];
      rd_dat16 <= BOUT[(addr[2:1] << 4) +:16];
      PAGE_AGE[page] <= 16'b0;
      rd_pend <= 8'hff;
      BRAM_CLK <= 1'b1;
      phase <= 2'b11;
   end
end endtask

task get8(reg [28:0] addr);
// Get a 8 bit value if available in cache
begin
   find_page_a(addr);
   if (!signal[0]) begin
      BWE <= 0;
      BPAGE <= page;
      BADDR <= addr[9:3];
      rd_dat8 <= BOUT[(addr[2:0] << 3) +:8];
      PAGE_AGE[page] <= 16'b0;
      rd_pend <= 8'hff;
      BRAM_CLK <= 1'b1;
      phase <= 2'b11;
   end
end endtask

task put16(reg [28:0] addr, reg [15:0] val);
// Put a 16 bit value if its page is cached.
begin
   find_page_a(addr);
   if (!signal[0]) begin
      BPAGE <= page;
      BADDR <= addr[9:3];
      BIN[(addr[2:1] << 4) +:16] <= val;
      BWE <= 1;
      PAGE_AGE[page] <= 16'b0;
      PAGE_W[page] <= 1'b1;
      wr_pend <= 8'hff;
      BRAM_CLK <= 1'b1;
      phase <= 2'b11;
   end else signal[0] <= 1'b1;
end endtask

task put8(reg [28:0] addr, reg [7:0] val);
// Put an 8 bit value if its page is cached.
begin
   find_page_a(addr);
   if (!signal[0]) begin
      BPAGE <= page;
      BADDR <= addr[9:3];
      BIN[(addr[2:1] << 3) +:8] <= val;
      BWE <= 1;
      PAGE_W[page] <= 1'b1;
      PAGE_AGE[page] <= 16'b0;
      wr_pend <= 8'hff;
      BRAM_CLK <= 1'b1;
      phase <= 2'b11;
   end else signal[0] <= 1'b1;
end endtask

task write_page_s(reg [6:0] p);
// Begin process of sending a cache page to ddr3
begin
   page         <= p;
   BWE         <= 0;
   BPAGE       <= p;
   BADDR       <= 0;
   ram_address <= PAGE_ADDR[page];
   ram_burst   <= 8'd128;
   wr_pend     <= 8'd127;
   BRAM_CLK      <= 1;
   wr_acki       <= 0;
   ram_write   <= 0;
end endtask

task write_page_l(reg [6:0] p);
// Continue write burst
begin
   if (!DDRAM_BUSY && !wr_pend[7]) begin
      BADDR       <= ~wr_pend[6:0];
      ram_data    <= BOUT;
      BRAM_CLK      <= 1'b0;
      ram_write   <= 1'b1;
   end
end endtask

task write_page_h(reg [6:0] p);
// Continue write burst
begin
   if (!DDRAM_BUSY && !wr_pend[7]) begin
      wr_pend      <= wr_pend - 8'd1;
      BRAM_CLK      <= 1'b1;
      ram_write   <= 1'b0;
   end
end endtask

task reset_page(reg [6:0] p, reg [15:0] addr, reg ptype=1'b1);
// Reset the page and set a new address for it.
begin
   page               <= p;
   PAGE_ADDR[page]   <= addr;
   PAGE_AGE[page]      <= 16'b0;
   PAGE_SWAP[page]   <= ptype;
   PAGE_W[page]      <= 1'b0;
end endtask

task read_page_s(reg [6:0] p);
// Begin process of getting a cache page from ddr3
begin
   page         <= p;
   BWE         <= 1;
   BPAGE       <= p;
   BADDR       <= 0;
   ram_burst   <= 8'd128;
   rd_pend     <= 8'd127;
   BRAM_CLK      <= 1'b0;
   ram_read      <= 1'b1;
   rd_acki       <= 0;
end endtask

task read_page_l(reg [6:0] p);
// Continue read burst
begin
   if (!DDRAM_BUSY && DDRAM_DOUT_READY && !rd_pend[7]) begin
      BADDR       <= ~rd_pend[6:0];
      BIN          <= DDRAM_DOUT;
      BRAM_CLK      <= 1'b1;
      ram_read      <= 1'b0;
   end
end endtask

task read_page_h(reg [6:0] p);
// Continue read burst
begin
   if (!DDRAM_BUSY && DDRAM_DOUT_READY && !rd_pend[7]) begin
      rd_pend       <= rd_pend - 8'd1;
      BRAM_CLK      <= 1'b0;
      ram_read      <= 1'b1;
   end
end endtask

task page_sw_s;
// Begin process of swapping a page out and bringing a new one in.
// Page swapped out will be the least accessed page.
reg [7:0] p;
begin
   // We only have to write the page if its been updated
   find_page_old;
   sw_pend <= (PAGE_W[page] == 1'b1) ? 2'b01 : 2'b10;
   sw_page <= page;
end endtask

// CFG interface:
// Use cfg_req:1 with rd_req:0 and wr_req:0 for the following tasks:
// cfg_req:1 wr_d:16'd0 wr_d8:<page>: Make a page unswappable
// cfg_req:1 wr_d:16'd1 wr_d8:<page>: Make a page swappable
// cfg_req:1 wr_d:16'd2               Look up a page by address (returned in rd_d8)
// cfg_req:1 wr_d:16'd3 wr_d8:<page> wr_a:<addr> Assign an address to a page
// cfg_req:1 wr_d:16'd4 wr_d8:<page> Make a page young (age = 0)
// cfg_req:1 wr_d:16'd5 wr_d8:<page> Make a page old (age = 65535)
// cfg_req:1 wr_d:16'd7 wr_d8:<page> wr_a:<addr> Reset a page (does not commit it!)
   
task cfg(reg [28:0] a, reg [15:0] f, reg [6:0] p);
begin
   debug <= 8'b11100000;
   case (f)
      16'd0: PAGE_SWAP[p] <= 1'b0;
      16'd1: PAGE_SWAP[p] <= 1'b1;
      16'd2: begin
         find_page_a(a);
         rd_dat8 <= page;
         end
      16'd3: PAGE_ADDR[p] <= a;
      16'd4: PAGE_AGE[p] <= 16'h0;
      16'd5: PAGE_AGE[p] <= 16'hFFFF;
      16'd7: reset_page(p, a, 1'b1);
      default: ;
   endcase      
end endtask

// Phases:
// 00: Ready for r/w request
// 01: Initialize page swap
// 02: Continue page swap
// 03: Finalize transfer (wait for rd/wr req to go low

reg [7:0] pg = 0;
reg init = 0;
always @(posedge DDRAM_CLK) begin
   if (!init) begin
      for (pg=0; pg < 128; pg++) begin // Cheap ass init block
         PAGE_ADDR[pg] <= 28'b0;
         PAGE_AGE[pg] <= 16'b0;
         PAGE_W[pg] <= 1'b0;
         PAGE_SWAP[pg] <= 1'b1;
      end
      rd_pend <= 8'hFF;
      wr_pend <= 8'hFF;
      phase <= 2'b00;
      init <= 1;
   end
   debug[6:5] <= phase;
   age_pages;
   if (phase == 2'b00) begin // Should be in phase 0 whenever we are ready to process requests.
      if (wr_req) begin
         debug[7] <= 1'b1;
         if (bus16)
            case (wr_be)
               2'b00: ;
               2'b01: put8(wr_a,wr_d[7:0]);
               2'b10: put8(wr_a+1,wr_d[15:8]);
               2'b11: put16(wr_a,wr_d);
            endcase
         else
            put8(wr_a, wr_d8);
         if (signal[0]) begin // Page not found
            page_sw_s;
            sw_page <= page;
            debug[4:0] <= {3'b001,sw_pend};
            case (sw_pend)
               2'b01: write_page_s(sw_page);
               2'b10: read_page_s(sw_page);
               default: ;
            endcase         
            phase <= 2'b01;
            debug[4:0] <= {3'b001,sw_pend};
         end else begin
            phase <= 2'b11;
         end;
      end else if (rd_req) begin
         debug[7] <= 1'b0;
         if (bus16)    get16(rd_a);
         else          get8(rd_a);
         if (signal[0]) begin
            page_sw_s;
            sw_page <= page;
            case (sw_pend)
               2'b01: write_page_s(sw_page);
               2'b10: read_page_s(sw_page);
               default: ;
            endcase         
            phase <= 2'b01;
            debug[4:0] <= {3'b001,sw_pend};
         end else phase <= 2'b11;   
      end else if (!wr_req && !rd_req && cfg_req) begin
         debug <= 8'b11100111;
         cfg(wr_a, wr_d, wr_d8);
         cfg_req <= 1'b0;
         phase <= 2'b10;
      end
   end else if (phase == 2'b01) begin // Phase 1: Swap page
      case (sw_pend)
         2'b01: write_page_l(sw_page);
         2'b10: begin // Reset page and set address
            if (rd_req)
               reset_page(sw_page,rd_a);
            else
               reset_page(sw_page,wr_a);            
         end
         2'b11: begin // Read page from DDR3
            read_page_l(sw_page);
         end
         default: ;
      endcase
      phase <= 2'b10;
      debug <= {6'b101001,sw_pend};
   end else if (phase == 2'b10 || (!rd_req && !wr_req)) begin // Continue or end decision
      case (sw_pend)
         2'b01: begin // Commit page to DDR3 (Skipped if no writes to the page)
            write_page_h(sw_page);
            if (wr_pend[7]) sw_pend <= 2'b10;
         end
         2'b10: begin // Reset page and set address
            read_page_s(sw_page);
            sw_pend <= 2'b11;
         end
         2'b11: begin // Read page from DDR3
            read_page_h(sw_page);
            if (rd_pend[7]) sw_pend <= 2'b00;
         end
         default: ;
      endcase
      BRAM_CLK <= 1'b0;
      if (rd_pend != 8'hFF || wr_pend != 8'hFF) begin
         phase <= 2'b01; // DDR3 burst continues
      end else begin   
         phase <= 2'b00; // Retry RW
      end
      
   end else if (phase == 2'b11 || (!rd_req && !wr_req)) begin
      // Wait for the core to be ready
      // We also detect if theres no request being made
      // to allow the phase to be reset to 0.
      signal <= 8'b00000000;
      BWE <= 1'b0;
      BRAM_CLK <= 1'b0;
      if (rd_pend==8'hFF)   rd_acki <= 1;
      if (wr_pend==8'hFF)   wr_acki <= 1;
   end
   if (!wr_req)         wr_acki <= 0;
   if (!rd_req)         rd_acki <= 0;
   if (!wr_req && !rd_req) phase <= 2'b00;
end
endmodule


I found out theres something very broken about how i'm trying to drive the BRAM. Consider this code 'conceptual' for now and don't try to use it lol.


I'm going to start developing a platform from scratch today and drop this module in it and fix it up. I'm also going to work on it in Minimig, but something LIKE this module is what I am going with for the 256M RAM expansion, so i'll keep this board/thread updated when it is working.

It may... MAY greatly mitigate latency issues on DDR3 but it is not a full solution. I don't think there is a full solution, but at the very least it might make the DDR3 usable in more situations.


Return to “MiSTer”

Who is online

Users browsing this forum: No registered users and 2 guests