mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Merge branch 'feat/spi_master_ddr_mode' into 'master'
feat(driver_spi): master driver support DDR(DTR) clock mode Closes IDFGH-12948 See merge request espressif/esp-idf!49810
This commit is contained in:
@@ -118,6 +118,7 @@ typedef struct {
|
||||
#define SPI_TRANS_MULTILINE_ADDR SPI_TRANS_MODE_DIOQIO_ADDR ///< The data lines used at address phase is the same as data phase (otherwise, only one data line is used at address phase)
|
||||
#define SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL (1<<11) ///< By default driver will automatically re-alloc dma buffer if it doesn't meet hardware alignment or dma_capable requirements, this flag is for you to disable this feature, you will need to take care of the alignment otherwise driver will return you error ESP_ERR_INVALID_ARG
|
||||
#define SPI_TRANS_DMA_USE_PSRAM (1<<12) ///< Use PSRAM for DMA buffer directly, has speed limit, but no temp buffer and save memory
|
||||
#define SPI_TRANS_DDRCLK (1<<13) ///< Use DDRCLK (double clock edge) for current transaction.
|
||||
|
||||
// Output flags
|
||||
#define SPI_TRANS_DMA_RX_FAIL (1<<30) ///< RX transaction data lose flag, indicate DMA RX overflow
|
||||
|
||||
@@ -649,6 +649,17 @@ int spi_get_actual_clock(int fapb, int hz, int duty_cycle)
|
||||
return spi_hal_master_cal_clock(fapb, hz, duty_cycle);
|
||||
}
|
||||
|
||||
static SPI_MASTER_ISR_ATTR bool s_spi_clock_need_reconfig(spi_device_t *dev, spi_trans_priv_t *trans_buf)
|
||||
{
|
||||
if (!trans_buf) {
|
||||
return false;
|
||||
}
|
||||
if (dev->hal_dev.timing_conf.use_ddr_clk != !!(trans_buf->trans->flags & SPI_TRANS_DDRCLK)) {
|
||||
return true;
|
||||
}
|
||||
return ((trans_buf->trans->override_freq_hz > 0) && (dev->hal_dev.timing_conf.expect_freq != trans_buf->trans->override_freq_hz));
|
||||
}
|
||||
|
||||
// Setup the device-specified configuration registers. Called every time a new
|
||||
// transaction is to be sent, but only apply new configurations when the device
|
||||
// changes or timing change is required.
|
||||
@@ -659,21 +670,31 @@ static SPI_MASTER_ISR_ATTR void spi_setup_device(spi_device_t *dev, spi_trans_pr
|
||||
spi_hal_dev_config_t *hal_dev = &(dev->hal_dev);
|
||||
|
||||
bool clock_changed = false;
|
||||
// check if timing config update is required
|
||||
if (trans_buf && (trans_buf->trans->override_freq_hz > 0) && (hal_dev->timing_conf.expect_freq != trans_buf->trans->override_freq_hz)) {
|
||||
if (s_spi_clock_need_reconfig(dev, trans_buf)) { // check if timing config update is required
|
||||
const bool want_ddr = !!(trans_buf->trans->flags & SPI_TRANS_DDRCLK);
|
||||
uint32_t target_freq = trans_buf->trans->override_freq_hz ? trans_buf->trans->override_freq_hz : dev->cfg.clock_speed_hz;
|
||||
spi_hal_timing_param_t timing_param = {
|
||||
.expected_freq = trans_buf->trans->override_freq_hz,
|
||||
.use_ddr_clk = want_ddr,
|
||||
.expected_freq = target_freq,
|
||||
.clk_src_hz = dev->hal_dev.timing_conf.source_real_freq,
|
||||
.duty_cycle = dev->cfg.duty_cycle_pos,
|
||||
.input_delay_ns = dev->cfg.input_delay_ns,
|
||||
.half_duplex = dev->hal_dev.half_duplex,
|
||||
.no_compensate = dev->hal_dev.no_compensate,
|
||||
.use_gpio = !(dev->host->bus_attr->flags & SPICOMMON_BUSFLAG_IOMUX_PINS),
|
||||
};
|
||||
|
||||
if ((trans_buf->trans->override_freq_hz <= SPI_PERIPH_SRC_FREQ_MAX) && (ESP_OK == spi_hal_cal_clock_conf(&timing_param, &dev->hal_dev.timing_conf))) {
|
||||
if ((target_freq <= SPI_PERIPH_SRC_FREQ_MAX) && (ESP_OK == spi_hal_cal_clock_conf(&timing_param, &dev->hal_dev.timing_conf))) {
|
||||
clock_changed = true;
|
||||
} else {
|
||||
ESP_EARLY_LOGW(SPI_TAG, "assigned override_freq_hz %d not supported", trans_buf->trans->override_freq_hz);
|
||||
// Keep previous timing_conf; clarify why reconfig failed.
|
||||
if (want_ddr != !!dev->hal_dev.timing_conf.use_ddr_clk) {
|
||||
ESP_EARLY_LOGW(SPI_TAG, "failed to switch to %s at %lu Hz, keep previous clock config",
|
||||
want_ddr ? "DDR" : "SDR", (unsigned long)target_freq);
|
||||
} else {
|
||||
ESP_EARLY_LOGW(SPI_TAG, "failed to apply override_freq_hz %lu, keep previous frequency",
|
||||
(unsigned long)target_freq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1116,6 +1137,9 @@ static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handl
|
||||
SPI_CHECK(!(host->id == SPI3_HOST && trans_desc->flags & SPI_TRANS_MODE_OCT), "SPI3 does not support octal mode", ESP_ERR_INVALID_ARG);
|
||||
SPI_CHECK(!((trans_desc->flags & SPI_TRANS_MODE_OCT) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "Incompatible when setting to both Octal mode and 3-wire-mode", ESP_ERR_INVALID_ARG);
|
||||
SPI_CHECK(!((trans_desc->flags & SPI_TRANS_MODE_OCT) && !is_half_duplex), "Incompatible when setting to both Octal mode and half duplex mode", ESP_ERR_INVALID_ARG);
|
||||
#endif
|
||||
#if !SOC_SPI_SUPPORT_DDR_CLOCK
|
||||
SPI_CHECK(!(trans_desc->flags & SPI_TRANS_DDRCLK), "DDRCLK is not supported on this chip", ESP_ERR_NOT_SUPPORTED);
|
||||
#endif
|
||||
SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO | SPI_TRANS_MODE_QIO)) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "Incompatible when setting to both multi-line mode and 3-wire-mode", ESP_ERR_INVALID_ARG);
|
||||
SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO | SPI_TRANS_MODE_QIO)) && !is_half_duplex), "Incompatible when setting to both multi-line mode and half duplex mode", ESP_ERR_INVALID_ARG);
|
||||
|
||||
@@ -21,6 +21,6 @@ endif()
|
||||
# the component can be registered as WHOLE_ARCHIVE
|
||||
idf_component_register(
|
||||
SRCS ${srcs}
|
||||
PRIV_REQUIRES esp_driver_spi spi_flash esp_timer esp_driver_gpio esp_mm
|
||||
PRIV_REQUIRES esp_driver_spi spi_flash esp_timer esp_driver_gpio esp_mm esp_driver_uart
|
||||
WHOLE_ARCHIVE
|
||||
)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/spi_slave.h"
|
||||
#include "driver/uart.h"
|
||||
#include "sys/param.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "hal/spi_ll.h" // for SPI_LL_SRC_PRE_DIV_MAX
|
||||
@@ -2192,3 +2193,60 @@ TEST_CASE("SPI_Master: PSRAM buffer transaction via EDMA", "[spi]")
|
||||
spi_bus_free(TEST_SPI_HOST);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_SPI_SUPPORT_DDR_CLOCK
|
||||
TEST_CASE("Test master cmd/data DDR/SDR", "[spi]")
|
||||
{
|
||||
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||
buscfg.miso_io_num = buscfg.mosi_io_num; // same pin to test data loopback
|
||||
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_DISABLED));
|
||||
|
||||
spi_device_handle_t dev0;
|
||||
spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
|
||||
devcfg.command_bits = 16;
|
||||
devcfg.address_bits = 16;
|
||||
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0));
|
||||
|
||||
// Tap SCLK with UART bitrate detection to count clock edges.
|
||||
uart_bitrate_detect_config_t uart_cfg = {
|
||||
.rx_io_num = buscfg.sclk_io_num,
|
||||
.source_clk = UART_SCLK_DEFAULT,
|
||||
};
|
||||
|
||||
spi_transaction_t trans_cfg = {
|
||||
.cmd = 0x1234,
|
||||
.addr = 0x5678,
|
||||
.length = 32,
|
||||
.tx_data = {0xAA, 0x34, 0x56, 0x5f},
|
||||
};
|
||||
uint32_t clk_edges[2];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
const bool use_ddr = (i % 2) != 0;
|
||||
printf("\nTest trans %s\n", use_ddr ? "DDR" : "SDR");
|
||||
trans_cfg.flags = use_ddr ? SPI_TRANS_DDRCLK : 0;
|
||||
trans_cfg.flags |= SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA;
|
||||
trans_cfg.tx_data[3] *= 2;
|
||||
memset(trans_cfg.rx_data, 0, sizeof(trans_cfg.rx_data));
|
||||
|
||||
TEST_ESP_OK(uart_detect_bitrate_start(UART_NUM_1, &uart_cfg));
|
||||
TEST_ESP_OK(spi_device_polling_transmit(dev0, &trans_cfg));
|
||||
uart_bitrate_res_t uart_res = {};
|
||||
TEST_ESP_OK(uart_detect_bitrate_stop(UART_NUM_1, true, &uart_res));
|
||||
|
||||
int measured_freq_khz = (uart_res.clk_freq_hz / uart_res.pos_period) / 1000;
|
||||
printf("clk edge %lu, measured freq %d kHz\n", uart_res.edge_cnt, measured_freq_khz);
|
||||
clk_edges[i % 2] = uart_res.edge_cnt;
|
||||
int bit_num = devcfg.command_bits + devcfg.address_bits + trans_cfg.length;
|
||||
// SDR: one bit per clock period => 2 edges/bit; DDR: two bits per period => 1 edge/bit
|
||||
TEST_ASSERT_INT_WITHIN(5, use_ddr ? bit_num : bit_num * 2, (int)uart_res.edge_cnt);
|
||||
TEST_ASSERT_INT_WITHIN(devcfg.clock_speed_hz / 100000, measured_freq_khz, devcfg.clock_speed_hz / 1000);
|
||||
ESP_LOG_BUFFER_HEX("Tx", trans_cfg.tx_data, 4);
|
||||
ESP_LOG_BUFFER_HEX("Rx", trans_cfg.rx_data, 4);
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(trans_cfg.tx_data, trans_cfg.rx_data, 4);
|
||||
}
|
||||
TEST_ASSERT_INT_WITHIN(50, clk_edges[1] * 2, clk_edges[0]); // DDR should cost half clk edges of SDR
|
||||
|
||||
TEST_ESP_OK(spi_bus_remove_device(dev0));
|
||||
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1068,6 +1068,17 @@ static inline void spi_ll_set_addr_bitlen(spi_dev_t *hw, int bitlen)
|
||||
hw->user.usr_addr = bitlen ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DDR mode for the SPI.
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers.
|
||||
* @param enable True to enable DDR mode, false to disable.
|
||||
*/
|
||||
static inline void spi_ll_enable_ddr_mode(spi_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->misc.clk_data_dtr_en = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the address value in an intuitive way.
|
||||
*
|
||||
|
||||
@@ -1010,6 +1010,17 @@ static inline void spi_ll_set_addr_bitlen(spi_dev_t *hw, int bitlen)
|
||||
hw->user.usr_addr = bitlen ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DDR mode for the SPI.
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers.
|
||||
* @param enable True to enable DDR mode, false to disable.
|
||||
*/
|
||||
static inline void spi_ll_enable_ddr_mode(spi_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->misc.clk_data_dtr_en = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the address value in an intuitive way.
|
||||
*
|
||||
|
||||
@@ -1011,6 +1011,17 @@ static inline void spi_ll_set_addr_bitlen(spi_dev_t *hw, int bitlen)
|
||||
hw->user.usr_addr = bitlen ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DDR mode for the SPI.
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers.
|
||||
* @param enable True to enable DDR mode, false to disable.
|
||||
*/
|
||||
static inline void spi_ll_enable_ddr_mode(spi_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->misc.clk_data_dtr_en = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the address value in an intuitive way.
|
||||
*
|
||||
|
||||
@@ -1064,6 +1064,17 @@ static inline void spi_ll_set_addr_bitlen(spi_dev_t *hw, int bitlen)
|
||||
hw->user.usr_addr = bitlen ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DDR mode for the SPI.
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers.
|
||||
* @param enable True to enable DDR mode, false to disable.
|
||||
*/
|
||||
static inline void spi_ll_enable_ddr_mode(spi_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->misc.clk_data_dtr_en = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the address value in an intuitive way.
|
||||
*
|
||||
|
||||
@@ -53,6 +53,7 @@ typedef struct {
|
||||
* Left 0 if not known.
|
||||
*/
|
||||
bool use_gpio; ///< True if the GPIO matrix is used, otherwise false
|
||||
bool use_ddr_clk; ///< Whether to use DDR mode for clock
|
||||
} spi_hal_timing_param_t;
|
||||
|
||||
/**
|
||||
@@ -70,6 +71,7 @@ typedef struct {
|
||||
int timing_dummy; ///< Extra dummy needed to compensate the timing
|
||||
int timing_miso_delay; ///< Extra miso delay clocks to compensate the timing
|
||||
spi_sampling_point_t rx_sample_point;///< Sample data follow standard SPI timing in master mode
|
||||
bool use_ddr_clk; ///< Whether to use DDR mode for clock
|
||||
} spi_hal_timing_conf_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,13 +37,18 @@ void spi_hal_setup_device(spi_hal_context_t *hal, const spi_hal_dev_config_t *de
|
||||
spi_ll_master_set_cs_setup(hw, dev->cs_setup);
|
||||
spi_ll_master_set_cs_hold(hw, dev->cs_hold);
|
||||
spi_ll_master_select_cs(hw, dev->cs_pin_id);
|
||||
#if SOC_SPI_SUPPORT_DDR_CLOCK
|
||||
spi_ll_enable_ddr_mode(hw, dev->timing_conf.use_ddr_clk);
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t spi_hal_cal_clock_conf(const spi_hal_timing_param_t *timing_param, spi_hal_timing_conf_t *timing_conf)
|
||||
{
|
||||
spi_ll_clock_val_t reg_val;
|
||||
int dummy, miso_delay;
|
||||
int eff_clk_n = spi_ll_master_cal_clock(timing_param->clk_src_hz, timing_param->expected_freq, timing_param->duty_cycle, ®_val);
|
||||
// Hardware halves the output frequency when DDR is enabled; calculate with 2x so the effective rate stays the same.
|
||||
uint32_t expected_freq = timing_param->use_ddr_clk ? timing_param->expected_freq * 2 : timing_param->expected_freq;
|
||||
int eff_clk_n = spi_ll_master_cal_clock(timing_param->clk_src_hz, expected_freq, timing_param->duty_cycle, ®_val);
|
||||
|
||||
//When the speed is too fast, we may need to use dummy cycles to compensate the reading.
|
||||
//But these don't work for full-duplex connections.
|
||||
@@ -63,8 +68,10 @@ esp_err_t spi_hal_cal_clock_conf(const spi_hal_timing_param_t *timing_param, spi
|
||||
|
||||
if (timing_conf) {
|
||||
timing_conf->clock_reg = reg_val;
|
||||
timing_conf->use_ddr_clk = timing_param->use_ddr_clk;
|
||||
timing_conf->expect_freq = timing_param->expected_freq;
|
||||
timing_conf->real_freq = eff_clk_n;
|
||||
// eff_clk_n was calculated for 2x in DDR; report the effective data rate to the user.
|
||||
timing_conf->real_freq = timing_param->use_ddr_clk ? eff_clk_n / 2 : eff_clk_n;
|
||||
timing_conf->timing_dummy = dummy;
|
||||
timing_conf->timing_miso_delay = miso_delay;
|
||||
}
|
||||
|
||||
@@ -1339,6 +1339,10 @@ config SOC_SPI_SUPPORT_OCT
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_SPI_SUPPORT_DDR_CLOCK
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_SPIRAM_XIP_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
@@ -507,6 +507,7 @@
|
||||
#define SOC_SPI_SUPPORT_SLEEP_RETENTION 1
|
||||
#define SOC_SPI_SUPPORT_SLAVE_HD_VER2 1
|
||||
#define SOC_SPI_SUPPORT_OCT 1
|
||||
#define SOC_SPI_SUPPORT_DDR_CLOCK 1
|
||||
|
||||
/*-------------------------- SPIRAM CAPS ----------------------------------------*/
|
||||
#define SOC_SPIRAM_XIP_SUPPORTED 1
|
||||
|
||||
@@ -571,6 +571,10 @@ config SOC_SPI_SUPPORT_OCT
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_SPI_SUPPORT_DDR_CLOCK
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_MEMSPI_SUPPORT_CONTROL_DUMMY_OUT
|
||||
bool
|
||||
default y
|
||||
|
||||
@@ -262,6 +262,7 @@
|
||||
#define SOC_SPI_SUPPORT_SLAVE_HD_VER2 1
|
||||
#define SOC_SPI_HD_BOTH_INOUT_SUPPORTED 1 //Support enabling MOSI and MISO phases together under Halfduplex mode
|
||||
#define SOC_SPI_SUPPORT_OCT 1
|
||||
#define SOC_SPI_SUPPORT_DDR_CLOCK 1
|
||||
|
||||
// Peripheral supports output given level during its "dummy phase"
|
||||
// Only SPI1 supports this feature
|
||||
|
||||
@@ -675,6 +675,10 @@ config SOC_SPI_SUPPORT_OCT
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_SPI_SUPPORT_DDR_CLOCK
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_SPIRAM_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
@@ -282,6 +282,7 @@
|
||||
#define SOC_SPI_MAXIMUM_BUFFER_SIZE 64
|
||||
#define SOC_SPI_SUPPORT_SLAVE_HD_VER2 1
|
||||
#define SOC_SPI_SUPPORT_OCT 1
|
||||
#define SOC_SPI_SUPPORT_DDR_CLOCK 1
|
||||
|
||||
/*-------------------------- SPIRAM CAPS ----------------------------------------*/
|
||||
#define SOC_SPIRAM_SUPPORTED 1
|
||||
|
||||
@@ -891,6 +891,10 @@ config SOC_SPI_SUPPORT_OCT
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_SPI_SUPPORT_DDR_CLOCK
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_SPIRAM_XIP_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
@@ -332,6 +332,7 @@
|
||||
#define SOC_SPI_SUPPORT_SLEEP_RETENTION 1
|
||||
#define SOC_SPI_SUPPORT_SLAVE_HD_VER2 1
|
||||
#define SOC_SPI_SUPPORT_OCT 1
|
||||
#define SOC_SPI_SUPPORT_DDR_CLOCK 1
|
||||
|
||||
/*-------------------------- SPIRAM CAPS ----------------------------------------*/
|
||||
#define SOC_SPIRAM_XIP_SUPPORTED 1
|
||||
|
||||
@@ -264,6 +264,13 @@ Supported line modes for {IDF_TARGET_NAME} are listed as follows, to make use of
|
||||
SPI_TRANS_MULTILINE_CMD
|
||||
- SPICOMMON_BUSFLAG_OCTAL
|
||||
|
||||
.. only:: SOC_SPI_SUPPORT_DDR_CLOCK
|
||||
|
||||
DDR Clock
|
||||
^^^^^^^^^
|
||||
|
||||
Set :c:macro:`SPI_TRANS_DDRCLK` in :cpp:member:`spi_transaction_t::flags` to use double data rate mode for the current transaction. In this mode, the command, address, and data phases are sent/sampled on both the rising and falling edges of the clock. If this flag is not set, the transaction uses the traditional single data rate mode. DDRCLK mode is supported in 2/4/8 line modes as well.
|
||||
|
||||
Command and Address Phases
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
@@ -264,6 +264,13 @@ SPI 总线传输事务由五个阶段构成,详见下表(任意阶段均可
|
||||
SPI_TRANS_MULTILINE_CMD
|
||||
- SPICOMMON_BUSFLAG_OCTAL
|
||||
|
||||
.. only:: SOC_SPI_SUPPORT_DDR_CLOCK
|
||||
|
||||
DDR 时钟
|
||||
^^^^^^^^
|
||||
|
||||
在 :cpp:member:`spi_transaction_t::flags` 中设置 :c:macro:`SPI_TRANS_DDRCLK`,可使当前传输事务使用时钟双边沿数据模式,该模式下, cmd/addr/data 段都将在时钟的上升沿和下降沿都进行传输,没有设置该标志时,传输将回到传统单边沿数据模式。DDRCLK 模式在 2/4/8 线模式下同样支持。
|
||||
|
||||
命令阶段和地址阶段
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
Reference in New Issue
Block a user