Sunday, November 19, 2023

An experiment just for fun.

I knew that "Digital" would enchant me. Especially for the fact that it gives me the opportunity to virtually replicate different areas of the Macintosh circuit to perform tests and understand how it works.

Now I would like to do an experiment. In truth, I don't need to go so low level for the emulator I have to write, but I'm curious to see how far this circuit simulator goes.

I want to try to implement the 64k-bit MT4264 DRAM memory chips present in the 128K Macintosh. So I found the datasheet, extracted the text of the description, and asked ChatGPT to synthesize the chip in Verilog naturally following the specifications.

What happened is interesting.

Hi, I want to build a DRAM module in Verilog. The DRAM size is 64k bits organized in 64k * 1. This is the description of the component in the datasheet. Can you write the Verilog module for me?

"The MT4264 is a randomly accessed solid-state memory containing 65,536 bits organized in a x1 configuration. During READ or WRITE cycles, each bit is uniquely ad- dressed through the 16 address bits, which are entered 8 bits (A0-A7) at a time. RAS is used to latch the first 8 bits and CAS the latter 8 bits. A READ or WRITE cycle is selected with the WE input. A logic HIGH on WE dictates READ mode while a logic LOW on WE dictates WRITE mode. During a WRITE cycle, data in (D) is latched by the falling edge of WE or CAS, whichever occurs last. If WE goes LOW prior to CAS going LOW, the output pin(s) remain open (High-Z) until the next CAS cycle. If WE goes LOW after data reaches the output pin(s), data out (Q) is activated and retains the selected cell data as long as CAS remains LOW (regardless of WE or RAS). This late WE pulse results in a READ-WRITE cycle. PAGE MODE operations allow faster data operations (READ, WRITE or READ-MODIFY-WRITE) within a row address (A0-A7) defined page boundary. The PAGE MODE cycle is always initiated with a row address strobed-in by RAS followed by a column address strobed-in by CAS. CAS may be toggled-in by holding RAS LOW and strobing- in different column addresses, thus executing faster memory cycles. Returning RAS HIGH terminates the PAGE MODE operation. Returning RAS and CAS HIGH terminates a memory cycle and decreases chip current to a reduced standby level. Also, the chip is preconditioned for the next cycle during the RAS HIGH time. Memory cell data is retained in its correct state by maintaining power and executing any RAS cycle (READ, WRITE, RAS-ONLY or HIDDEN RE- FRESH) so that all 256 combinations of RAS addresses (A0- A7) are executed at least every 4ms, regardless of sequence"

Creating a Verilog module for a DRAM such as the MT4264 involves simulating the behavior of the DRAM as described in your datasheet. In this simulation, you'll model the address latching with RAS and CAS, the READ and WRITE operations controlled by the WE signal, the data input and output handling, PAGE MODE operations, and the necessary timing for refresh cycles.

Here's a basic structure for such a module. This code is a simplified representation and may need adjustments based on the specific requirements of your system, such as timing constraints and interface details.

module MT4264 (
    input wire clk,        // System clock
    input wire rst,        // System reset
    input wire [7:0] addr, // 8-bit address input
    input wire ras,        // Row Address Strobe
    input wire cas,        // Column Address Strobe
    input wire we,         // Write Enable
    inout wire data        // Data line (bidirectional)
);

    // Memory array
    reg [0:0] memory [0:65535];
    reg [15:0] full_address;
    reg data_out;
    wire data_in;

    // Bidirectional data line
    assign data = (!we && !cas) ? data_out : 1'bz;
    assign data_in = data;

    // Latching addresses
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            full_address <= 16'd0;
        end else begin
            if (!ras) full_address[15:8] <= addr; // Latch row address
            if (!cas) full_address[7:0] <= addr;  // Latch column address
        end
    end

    // Read/Write operations
    always @(posedge clk) begin
        if (!cas && !we) begin
            // Write operation
            memory[full_address] <= data_in;
        end else if (!cas && we) begin
            // Read operation
            data_out <= memory[full_address];
        end
    end

    // Refresh logic (simplified)
    // In real applications, refresh logic must cycle through all row addresses
    // within the required refresh interval (e.g., every 4ms).

endmodule

This module covers the basic address latching with RAS and CAS, and the READ/WRITE operations based on the WE signal. The "memory" array represents the DRAM cells. The actual refresh logic and PAGE MODE operations are not implemented in this example and would need to be added based on your specific requirements.

Remember that this is a simplified model and may not accurately represent all nuances of the real DRAM behavior, especially with regard to timing constraints and electrical characteristics. It's suitable for simulation purposes but might need adjustments for use in real hardware or more detailed simulations.

The data signal is not bidirectional. Can you split it into signal D as input data, and Q as output data.

Certainly, splitting the data signal into separate input and output lines makes sense for clarity and alignment with typical DRAM module designs. Here's the revised Verilog module with "D" as the input data signal and "Q" as the output data signal:

module MT4264 (
    input wire clk,        // System clock
    input wire rst,        // System reset
    input wire [7:0] addr, // 8-bit address input
    input wire ras,        // Row Address Strobe
    input wire cas,        // Column Address Strobe
    input wire we,         // Write Enable
    input wire d,          // Data input
    output reg q           // Data output
);

    // Memory array
    reg [0:0] memory [0:65535];
    reg [15:0] full_address;

    // Latching addresses
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            full_address <= 16'd0;
        end else begin
            if (!ras) full_address[15:8] <= addr; // Latch row address
            if (!cas) full_address[7:0] <= addr;  // Latch column address
        end
    end

    // Read/Write operations
    always @(posedge clk) begin
        if (!cas && !we) begin
            // Write operation
            memory[full_address] <= d;
        end else if (!cas && we) begin
            // Read operation
            q <= memory[full_address];
        end
    end

    // Initialize the output
    initial begin
        q = 1'b0;
    end

    // Refresh logic (simplified)
    // In real applications, refresh logic must cycle through all row addresses
    // within the required refresh interval (e.g., every 4ms).

endmodule

In this version, "D" is used to write data into the memory array during write operations, and "Q" is used to hold the data read from the memory array during read operations. This separation aligns better with the actual DRAM interface, where data input and output are typically on separate lines.

Again, keep in mind that this is a basic model intended for simulation purposes and does not include all aspects of a real DRAM, such as detailed timing behavior, PAGE MODE operations, and comprehensive refresh logic.

by the way, signals RAS, CAS and WE are active low.

Thank you for clarifying that RAS, CAS, and WE are active-low signals. In Verilog, this means we should treat these signals as being active or 'asserted' when they are at a logical low level ("0"). I'll modify the code to reflect this behavior:

module MT4264 (
    input wire clk,        // System clock
    input wire rst,        // System reset
    input wire [7:0] addr, // 8-bit address input
    input wire ras_n,      // Row Address Strobe (active low)
    input wire cas_n,      // Column Address Strobe (active low)
    input wire we_n,       // Write Enable (active low)
    input wire d,          // Data input
    output reg q           // Data output
);

    // Memory array
    reg [0:0] memory [0:65535];
    reg [15:0] full_address;

    // Latching addresses
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            full_address <= 16'd0;
        end else begin
            if (ras_n == 1'b0) full_address[15:8] <= addr; // Latch row address
            if (cas_n == 1'b0) full_address[7:0] <= addr;  // Latch column address
        end
    end

    // Read/Write operations
    always @(posedge clk) begin
        if (cas_n == 1'b0 && we_n == 1'b0) begin
            // Write operation
            memory[full_address] <= d;
        end else if (cas_n == 1'b0 && we_n == 1'b1) begin
            // Read operation
            q <= memory[full_address];
        end
    end

    // Initialize the output-
    initial begin
        q = 1'b0;
    end

    // Refresh logic (simplified)
    // In real applications, refresh logic must cycle through all row addresses
    // within the required refresh interval (e.g., every 4ms).

endmodule

In this revised module, "ras_n", "cas_n", and "we_n" are treated as active-low signals. The code checks for "0" (low) to determine when these signals are active. This approach better matches the behavior described in the datasheet and is common in hardware design where active-low control signals are used.

Remember, this is still a basic simulation model. For practical hardware implementation, you'll need to consider the specific timing requirements and characteristics of the DRAM, which are not fully captured in this simple model.

Another thing, this DRAM is asynchronous so the clock signal is not necessary.

You're absolutely correct. Asynchronous DRAMs, like the one described in your datasheet, do not use a system clock signal for their operation. Their actions are controlled entirely by the RAS, CAS, and WE signals, along with the address inputs. I'll revise the Verilog module to reflect this asynchronous behavior:

module MT4264 (
    input wire rst,        // System reset
    input wire [7:0] addr, // 8-bit address input
    input wire ras_n,      // Row Address Strobe (active low)
    input wire cas_n,      // Column Address Strobe (active low)
    input wire we_n,       // Write Enable (active low)
    input wire d,          // Data input
    output reg q           // Data output
);

    // Memory array
    reg [0:0] memory [0:65535];
    reg [15:0] full_address;
    reg [7:0] row_address;
    reg [7:0] col_address;

    // Latching addresses on the falling edge of RAS and CAS
    always @(negedge ras_n or negedge cas_n) begin
        if (!ras_n) row_address <= addr; // Latch row address
        if (!cas_n) col_address <= addr; // Latch column address
    end

    // Combine row and column addresses
    always @(*) begin
        full_address = {row_address, col_address};
    end

    // Read/Write operations
    always @(*) begin
        if (!cas_n) begin
            if (!we_n) begin
                // Write operation
                memory[full_address] <= d;
            end else begin
                // Read operation
                q <= memory[full_address];
            end
        end
    end

    // Initialize the output
    initial begin
        q = 1'b0;
    end

    // Refresh logic (simplified)
    // In real applications, refresh logic must cycle through all row addresses
    // within the required refresh interval (e.g., every 4ms).

endmodule

In this version, the "clk" input has been removed, and the operations are triggered by changes in the RAS and CAS signals (specifically their falling edges, which is typical for active-low control signals in asynchronous memory modules).

Since there's no clock signal, the operations are now handled by combinational logic ("always @(*)"). However, do note that this might lead to more power consumption in simulation as changes to the address or control lines would immediately reflect in the logic.

As always, this module is a simplified model and does not include specific timing behavior, detailed PAGE MODE operations, or comprehensive refresh logic, which are important in a real-world implementation.

the last thing, the original component doesn't have the reset signal.

Understood. If the original DRAM component, such as the MT4264, does not include a reset signal, we should remove it from the Verilog module to accurately reflect its functionality. Here's the revised code without the reset signal:

module MT4264 (
    input wire [7:0] addr, // 8-bit address input
    input wire ras_n,      // Row Address Strobe (active low)
    input wire cas_n,      // Column Address Strobe (active low)
    input wire we_n,       // Write Enable (active low)
    input wire d,          // Data input
    output reg q           // Data output
);

    // Memory array
    reg [0:0] memory [0:65535];
    reg [7:0] row_address;
    reg [7:0] col_address;

    // Latching addresses on the falling edge of RAS and CAS
    always @(negedge ras_n or negedge cas_n) begin
        if (!ras_n) row_address <= addr; // Latch row address
        if (!cas_n) col_address <= addr; // Latch column address
    end

    // Combine row and column addresses
    always @(*) begin
        // Combine the latched row and column addresses to form the full address
        full_address = {row_address, col_address};
    end

    // Read/Write operations
    always @(*) begin
        if (!cas_n) begin
            if (!we_n) begin
                // Write operation
                memory[full_address] <= d;
            end else begin
                // Read operation
                q <= memory[full_address];
            end
        end
    end

    // Initialize the output
    initial begin
        q = 1'b0;
    end

    // Refresh logic (simplified)
    // In real applications, refresh logic must cycle through all row addresses
    // within the required refresh interval (e.g., every 4ms).

endmodule

In this version, the module no longer includes a "rst" (reset) input. The operations are controlled entirely by the address, RAS, CAS, and WE inputs. The memory is latched based on the state of the RAS and CAS signals, and read/write operations are performed accordingly.

Keep in mind that this is a basic model for simulation and conceptual understanding. Real-world DRAM behavior, especially concerning timing and refresh cycles, is more complex and would require additional logic and considerations in an accurate simulation or hardware implementation.

Uhm, I think you missed the full_address register

You're absolutely right. I apologize for that oversight. The "full_address" register is crucial for combining the row and column addresses. Let me correct that in the Verilog code:

module MT4264 (
    input wire [7:0] addr, // 8-bit address input
    input wire ras_n,      // Row Address Strobe (active low)
    input wire cas_n,      // Column Address Strobe (active low)
    input wire we_n,       // Write Enable (active low)
    input wire d,          // Data input
    output reg q           // Data output
);

    // Memory array
    reg [0:0] memory [0:65535];
    reg [7:0] row_address;
    reg [7:0] col_address;
    reg [15:0] full_address; // Combined row and column address

    // Latching addresses on the falling edge of RAS and CAS
    always @(negedge ras_n or negedge cas_n) begin
        if (!ras_n) row_address <= addr; // Latch row address
        if (!cas_n) col_address <= addr; // Latch column address
    end

    // Combine row and column addresses
    always @(*) begin
        full_address = {row_address, col_address};
    end

    // Read/Write operations
    always @(*) begin
        if (!cas_n) begin
            if (!we_n) begin
                // Write operation
                memory[full_address] <= d;
            end else begin
                // Read operation
                q <= memory[full_address];
            end
        end
    end

    // Initialize the output
    initial begin
        q = 1'b0;
    end

    // Refresh logic (simplified)
    // In real applications, refresh logic must cycle through all row addresses
    // within the required refresh interval (e.g., every 4ms).

endmodule

In this corrected version, "full_address" is correctly declared and used to combine the row and column addresses. This register is essential for addressing the correct memory cell within the 64K-bit array. Thank you for pointing out the mistake!

Remember that the A.I. it's not infallible, it's a risk to ask and copy/paste. The best intelligence is the one between the two ears!

Anyway, I tested the component in Verilog.

Unfortunately, by creating an array of 16 MT4264 DRAMs, the application struggles a bit in the initial compilation of the component. 

So, since it is not necessary at the moment, I will create two 64K*8bit memories using the native components which simulate a typical access of a dynamic RAM. 

0 Comments:

Post a Comment