Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //
- // ddram.v
- // Copyright (c) 2017 Sorgelig
- // Copyright (c) 2018 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/>.
- //
- // ------------------------------------------
- //
- // 8/16-bit version
- // Thank you @Sorgelig and the users on Smokemonster Discord for LOTS of ideas
- // Rysha, Grabulosaure, BrNX :) :)
- // Features:
- //
- // * 24 bytes of readahead cache
- // * 8 bytes of writeback cache
- // * Pauses only when it needs to commit a cache
- // * Leaves time for ascal to update the screen
- // * 8 or 16 bit write bus switchable dynamically
- // * Read 8 or 16 bit wide address anytime
- //
- // Caveats:
- // This module lets you access all 512m of the HPS DDR3.
- // $20000000-$3FFFFFFF
- // At the very least, ASCAL uses $20000000-$21FFFFFF
- // It is smart to reserve $20000000-$27FFFFFF for MiSTer functionality.
- // I recommend working your way down from the top of memory ($3FFFFFFF).
- module ddram
- (
- input DDRAM_CLK,
- input RESET,
- inout [7:0] debug,
- input DDRAM_BUSY, // waitrequest
- output [7:0] DDRAM_BURSTCNT, // burstcount
- output [28:0] DDRAM_ADDR, // address (in HPS reserved memory $20000000-$3FFFFFFF)
- input [63:0] DDRAM_DOUT, // readdata
- input DDRAM_DOUT_READY, // readdatavalid
- output DDRAM_RD, // read
- output [63:0] DDRAM_DIN, // readdata
- output [7:0] DDRAM_BE, // byteenable
- output DDRAM_WE, // write
- input bus16, // 0 = 8bit access through _d8, 1 = 16bit access through _d
- input [28:0] wr_a,
- input [15:0] wr_d,
- input [7:0] wr_d8,
- input [1:0] wr_be, // NOTE: Byte enable not used for d8 access!
- input wr_req,
- output wr_ack,
- input [28:0] rd_a,
- output [15:0] rd_d,
- output [7:0] rd_d8,
- input rd_req,
- output rd_ack
- );
- // I will probably be implementing a write cache.
- // Some of these new regs are for this.
- reg [7:0] ram_burst;
- reg [63:0] cache, wq, wq2, rq, rq2;
- reg [63:0] ram_data;
- reg [28:0] ram_address, cache_addr, w_addr, w2_a, t_a;
- reg ram_read = 0;
- reg ram_write = 0;
- reg [7:0] ram_be = 8'b11111111;
- reg [7:0] cache_wbe = 8'b00000000;
- reg [7:0] w_be = 8'b00000000;
- reg [7:0] w2_be = 8'b00000000;
- reg [15:0] t_d = 16'b0000000000000000;
- reg [7:0] t_d8 = 8'b00000000;
- reg [1:0] t_be = 0;
- reg wr_pend = 0;
- reg [1:0] rd_pend = 0;
- reg [1:0] rc_valid = 0;
- reg [1:0] w2_commit = 0;
- reg [3:0] ddr_wait = 0;
- reg rd_acki,wr_acki;
- assign rd_ack=rd_acki;
- assign wr_ack=wr_acki;
- // Constrain assignments to $20xxxxxx-$3Fxxxxxx. Lower addresses will echo to upper ones.
- // Addresses sent to this module represent their exact address in HPS RAM.
- assign DDRAM_ADDR = {ram_address[28:3],3'b000};
- assign DDRAM_BURSTCNT = ram_burst;
- assign DDRAM_BE = ram_be;
- assign DDRAM_RD = ram_read;
- assign DDRAM_DIN = ram_data;
- assign DDRAM_WE = ram_write;
- assign rd_d = cache[{rd_a[2:1], 4'b0000} +:16];
- assign rd_d8 = cache[{rd_a[2:0], 3'b000} +:8];
- always @(posedge DDRAM_CLK) begin
- ddr_wait <= DDRAM_BUSY ? 4'b0010 : (ddr_wait ? (ddr_wait - 4'b0001) : 4'b0000);
- debug[7] <= {(ddr_wait ? 1'b1:1'b0),7'b0000000}; // Bit 7 DDR_WAIT
- if (RESET) begin // Sane startup defaults
- rd_acki <= 0;
- wr_acki <= 0;
- wr_pend <= 0;
- ddr_wait <= 0;
- w2_commit <= 0;
- rc_valid <= 2'b00; // Ensure first read fills new cache
- cache_wbe <= 8'b00000000;
- cache <= {64{1'b0}};
- w_addr <= 29'h1FFFFFFF;
- cache_addr <= 29'h1FFFFFFF;
- end else begin
- if (rd_req) begin // Read requests
- // Stuff we can do any time.
- if (rc_valid == 2'b00) begin
- // Immediately deal with the condition of cache being invalid
- debug[3:0] <= 4'b0111; // $7: Invalid read cache
- cache_addr <= ({rd_a[28:3],3'b000});
- rd_acki <= 0;
- end else if ((cache_addr[28:3] == rd_a[28:3]) && (rc_valid >= 1)) begin
- // Read from read cache requires no action
- debug[3:0] <= 4'b0001; // $1: Cache hit
- rd_acki <= 1;
- end else if (((cache_addr[28:3]+1'd1) == rd_a[28:3]) && (rc_valid >= 2)) begin
- debug[3:0] <= 4'b0010; // $2: Cache+1 hit
- // Shift all caches down 1 and invalidate cache 3
- w_addr <= {cache_addr[28:3],3'b000};
- w_be <= cache_wbe;
- wq <= cache;
- cache <= rq;
- rq <= rq2;
- cache_addr <= {rd_a[28:3],3'b000};
- cache_wbe <= 8'b00000000;
- rc_valid <= 2;
- rd_acki <= 1;
- end else if (((cache_addr[28:3]+2'd2) == rd_a[28:3]) && (rc_valid == 3)) begin
- debug[3:0] <= 4'b0011; // $3: Cache+2 hit
- // Shift all caches down 2 invalidate cache 2 and 3
- w_addr <= {cache_addr[28:3],3'b000};
- w_be <= cache_wbe;
- wq <= cache;
- cache <= rq2;
- cache_addr <= {rd_a[28:3],3'b000};
- cache_wbe <= 8'b00000000;
- rc_valid <= 1;
- rd_acki <= 1;
- end else if (w_addr || (w2_commit!=2'b00) || wr_pend || (rd_pend != 2'b00)) begin
- debug[3:0] <= 4'b0110; // $6: Cache miss, in wait
- // If a read cannot be satisfied by cache we must pause
- rd_acki <= 0;
- rc_valid <= 0;
- end else debug[3:0] <= 4'b0000; // $0: Read request, nothing happened
- end
- if (wr_req) begin // Write requests
- debug[7] <= 0;
- // Stuff that can be done right away
- if (cache_addr[28:3] == wr_a[28:3]) begin
- // Write to read cache
- debug[3:0] <= 4'b1001; // $9: Read cache hit
- if (bus16) begin
- if (wr_be == 2'b00)
- cache_wbe <= cache_wbe | (8'b00000011 << (wr_a[2:1] << 1));
- else
- cache_wbe <= cache_wbe | ({6'b000000,wr_be} << (wr_a[2:1] << 1));
- cache[(wr_a[2:1] << 4) +:16] <= wr_d;
- end else begin
- cache_wbe[wr_a[2:0]] <= 1'b1;
- cache[(wr_a[2:0] << 3) +:8] <= wr_d8;
- end
- wr_acki <= 1;
- end else if (w2_a[28:3] == wr_a[28:3]) begin
- debug[3:0] <= 4'b1010; // $A: Write cache hit
- // Write to write cache
- if (bus16) begin
- if (wr_be == 2'b00)
- w2_be <= w2_be | (8'b00000011 << (wr_a[2:1] << 1));
- else
- w2_be <= w2_be | ({6'b000000,wr_be} << (wr_a[2:1] << 1));
- wq2[(wr_a[2:1] << 4) +:16] <= wr_d;
- end else begin
- w2_be[wr_a[2:0]] <= 1'b1;
- wq2[(wr_a[2:0] << 3) +:8] <= wr_d8;
- end
- wr_acki <= 1;
- end else if (!w2_commit) begin
- debug[3:0] <= 4'b1011; // $B: Write cache commit stage 1
- // Save the current transaction and commit write buffer
- t_d <= wr_d;
- t_d8 <= wr_d8;
- t_a <= wr_a;
- t_be <= wr_be;
- w2_commit <= 2;
- wr_acki <= 0;
- end else if (rd_pend || wr_pend || w_addr || w2_commit) begin
- debug[3:0] <= 4'b1100; // $C: Read/write cache miss
- // If a read or write is pending and we can't use the cache, we must pause.
- wr_acki <= 0;
- end else debug[3:0] <= 4'b1000; // $8: Write request, nothing happened
- end
- if (!ddr_wait) begin // Things we should do when the DDRAM is not busy
- // Signal end of read/write
- if (ram_write || ram_read) begin
- if (ram_write) begin
- ram_write <= 0;
- wr_pend <= 0;
- wr_acki <= 1;
- end
- if (ram_read) begin
- ram_read <= 0;
- end
- end
- if (!ram_write && w_addr) begin
- debug[6:4] <= 3'b010; // $2: Pipeline write commit
- // Commit the write pipeline cache
- ram_address <= w_addr;
- ram_be <= w_be;
- ram_data <= wq;
- ram_write <= 1;
- ram_burst <= 1;
- w_addr <= {29{1'b0}};
- wr_pend <= 1;
- end else if (!ram_read && rc_valid < 2'd3) begin
- debug[6:4] <= 1'b001; // $1 : Cache refill
- // Refill the cache when not busy
- ram_address <= {cache_addr[28:3]+rc_valid,3'b000};
- ram_be <= {8{1'b1}};
- ram_read <= 1;
- ram_burst <= (2'd3-rc_valid);
- rd_pend <= (2'd3-rc_valid);
- end else if (!ram_write && (w2_commit==2)) begin
- debug[6:4] <= 3'b011; // $3: Write cache commit stage 2
- // Commit the write buffer
- ram_address <= w2_a;
- ram_be <= w2_be;
- ram_data <= wq2;
- ram_write <= 1;
- ram_burst <= 1;
- w2_commit <= 1;
- wr_pend <= 1;
- end
- end else debug[6:4]=3'b000; // $0: DDR_WAIT | DDRAM_BUSY asserted
- end
- if (DDRAM_DOUT_READY) begin
- debug[6:4] <= 3'b100; // $4: DDRAM_DOUT_READY
- // Read data ready -- Refill cache
- rd_pend <= rd_pend - 1'b1;
- case (rc_valid)
- 0: cache <= DDRAM_DOUT;
- 1: rq <= DDRAM_DOUT;
- 2: rq2 <= DDRAM_DOUT;
- default: cache <= DDRAM_DOUT;
- endcase
- rc_valid <= rc_valid + 1'b1;
- rd_acki <= 1;
- end
- if (w2_commit==1) begin
- debug[6:4] <= 3'b101; // $5: New write cache
- // If Write buffer committed, make a new one.
- w2_a <= {t_a[28:3],3'b000};
- if (bus16) begin
- if (w2_be == 2'b00)
- w2_be <= (8'b00000011 << {t_a[2:1],1'b0});
- else
- w2_be <= ({6'b000000,t_be} << {t_a[2:1],1'b0});
- wq2 <= ({{48{1'b0}},t_d} << {t_a[2:1],4'b0000});
- end else begin
- w2_be <= 8'b00000001 << t_a[2:0];
- wq2 <= ({{56{1'b0}},t_d8} << {t_a[2:0],3'b000});
- end
- w2_commit <= 0;
- wr_acki <= 1;
- end
- if (!wr_req) wr_acki <= 0; // Drop my ack lines when
- if (!rd_req) rd_acki <= 0; // they drop their request
- end
- endmodule
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement