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

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

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

djsquare
Atari maniac
Atari maniac
Posts: 91
Joined: Thu Oct 18, 2018 2:01 am

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

Post by djsquare »

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: 253
Joined: Mon Apr 02, 2007 1:49 am

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

Post by remowilliams »

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 nerd
Atari nerd
Posts: 46
Joined: Wed Oct 03, 2018 2:26 pm
Contact:

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

Post by SmokeMonster »

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: 112
Joined: Sat Dec 29, 2018 5:46 pm

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

Post by bitfan2011 »

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
Atari nerd
Atari nerd
Posts: 47
Joined: Tue Oct 09, 2018 1:52 am

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

Post by ReedSolomon »

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_MiSTe ... tput_files
And he XML romset file here: https://github.com/furrtek/Neogeo_MiSTe ... omsets.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: 187
Joined: Fri Mar 16, 2018 9:10 am

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

Post by PhantombrainM »

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
Atari maniac
Atari maniac
Posts: 99
Joined: Fri Nov 23, 2018 2:01 pm

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

Post by crocky »

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: 4
Joined: Fri Dec 07, 2018 12:12 am

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

Post by Bartdesign »

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: 192
Joined: Wed Sep 26, 2018 6:03 am

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

Post by kitrinx »

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: 187
Joined: Fri Mar 16, 2018 9:10 am

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

Post by PhantombrainM »

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
Atari User
Atari User
Posts: 35
Joined: Sat Nov 03, 2018 6:21 am

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

Post by flain »

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?

Post by furrtek »

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?

Post by Tripmonkeyuk »

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
Atariator
Atariator
Posts: 29
Joined: Fri Oct 26, 2018 4:01 am

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

Post by Dubon »

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
Ultimate Atarian
Ultimate Atarian
Posts: 6348
Joined: Mon Dec 14, 2015 10:51 am
Location: Russia/Taiwan

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

Post by Sorgelig »

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?

Post by furrtek »

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?

Post by BeoWolff »

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
Ultimate Atarian
Ultimate Atarian
Posts: 6348
Joined: Mon Dec 14, 2015 10:51 am
Location: Russia/Taiwan

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

Post by Sorgelig »

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: 112
Joined: Sat Dec 29, 2018 5:46 pm

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

Post by bitfan2011 »

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: 187
Joined: Fri Mar 16, 2018 9:10 am

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

Post by PhantombrainM »

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
Captain Atari
Captain Atari
Posts: 267
Joined: Sun Sep 20, 2009 6:54 am
Location: FRANCE

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

Post by breiztiger »

@sorgelig

does the lastest version of MiSTer (main core) from today have special rom loading scheme from the one for neogeo core ?
Sorgelig
Ultimate Atarian
Ultimate Atarian
Posts: 6348
Joined: Mon Dec 14, 2015 10:51 am
Location: Russia/Taiwan

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

Post by Sorgelig »

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?

Post by furrtek »

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?

Post by Alynna »

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?

Post by Alynna »

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.
Locked

Return to “MiSTer”