From 0a992391d29f272a359994620543c918f5d039db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89der=20F=2E=20Zulian?= Date: Thu, 28 Jun 2018 14:35:14 +0200 Subject: [PATCH] Following changes: Show rgr related config during initialization. ORGR in traceAnalyzer. Submodule drampower set properly (point to rgr branch). New config for row increment (selective ref.). Specific simulations NO REF. and AR with close page policy. Simulation files ddr4 1, 2, 4 x mode open, close page policy, no ref, ar, rgr, orgr. New config for number of auto-ref. cmds in 64 ms. New traces for ddr4. New spec for dd4 16Gb after Christian's corrections. Initial offset for bankwise logic (if zeroed, for research). ORGR/RGR. Flex. ORGR/RGR. Bankwise flex. refresh. Small schanges. RGR flex test files. Doc updated. --- DRAMSys/docs/flexible-refresh.pdf | Bin 113272 -> 0 bytes DRAMSys/library/library.pro | 6 + .../orgr_ddr4_4x16Gbx16_dimm_p2KB_brc.xml | 20 ++ .../resources/configs/amconfigs/rgram.xml | 8 + .../configs/mcconfigs/fr_fcfs_bw_buffer16.xml | 16 ++ .../fr_fcfs_bw_buffer16_close_page.xml | 15 + .../mcconfigs/fr_fcfs_nbw_buffer16.xml | 54 ++++ .../fr_fcfs_nbw_buffer16_close_page.xml | 15 + .../resources/configs/mcconfigs/rgrmccfg.xml | 46 +++ ...SAMSUNG_K4B4G1646Q_4Gb_DDR3-1066_16bit.xml | 22 +- .../configs/memspecs/orgr_16Gb_ddr4.xml | 77 +++++ .../configs/memspecs/orgr_16Gb_ddr4_2x.xml | 77 +++++ .../configs/memspecs/orgr_16Gb_ddr4_4x.xml | 77 +++++ .../resources/configs/memspecs/rgrspec.xml | 84 ++++++ .../resources/configs/simulator/orgr.xml | 51 ++++ .../simulator/orgr_4b_opt_timings_ddr3.xml | 51 ++++ .../simulator/orgr_4b_std_timings_ddr3.xml | 51 ++++ .../simulator/orgr_8b_opt_timings_ddr3.xml | 51 ++++ .../simulator/orgr_8b_std_timings_ddr3.xml | 51 ++++ .../resources/configs/simulator/orgr_ddr4.xml | 18 ++ .../resources/configs/simulator/rgrsimcfg.xml | 18 ++ DRAMSys/library/resources/resources.pri | 54 ++++ .../library/resources/simulations/rgrsim.xml | 23 ++ DRAMSys/library/src/common/TlmRecorder.cpp | 4 + DRAMSys/library/src/common/protocol.h | 6 + .../library/src/common/third_party/DRAMPower | 2 +- DRAMSys/library/src/controller/Command.cpp | 26 +- DRAMSys/library/src/controller/Command.h | 21 +- DRAMSys/library/src/controller/Controller.cpp | 16 +- .../src/controller/ControllerState.cpp | 12 + .../library/src/controller/ControllerState.h | 1 + .../src/controller/core/ControllerCore.cpp | 31 +- .../src/controller/core/ControllerCore.h | 1 + .../src/controller/core/TimingCalculation.cpp | 6 +- .../core/configuration/Configuration.cpp | 75 ++++- .../core/configuration/Configuration.h | 35 +++ .../configuration/ConfigurationLoader.cpp | 2 - .../controller/core/configuration/MemSpec.h | 4 + .../controller/core/refresh/IRefreshManager.h | 13 +- .../src/controller/core/refresh/RGR.cpp | 266 ++++++++++++++++++ .../library/src/controller/core/refresh/RGR.h | 69 +++++ .../controller/core/refresh/RefreshManager.h | 9 - .../core/refresh/RefreshManagerBankwise.cpp | 179 ++++++++++-- .../core/refresh/RefreshManagerBankwise.h | 15 +- .../core/scheduling/checker/ActBChecker.cpp | 144 ++++++++++ .../core/scheduling/checker/ActBChecker.h | 56 ++++ .../scheduling/checker/ActivateChecker.cpp | 16 +- .../core/scheduling/checker/PreBChecker.cpp | 80 ++++++ .../core/scheduling/checker/PreBChecker.h | 51 ++++ .../checker/PrechargeAllChecker.cpp | 6 +- .../scheduling/checker/PrechargeChecker.cpp | 6 +- .../core/scheduling/checker/ReadChecker.cpp | 3 +- .../scheduling/checker/RefreshChecker.cpp | 10 +- .../core/scheduling/checker/WriteChecker.cpp | 3 +- DRAMSys/library/src/simulation/DRAMSys.cpp | 7 +- DRAMSys/library/src/simulation/Dram.h | 28 +- .../library/src/simulation/TracePlayer.cpp | 1 + DRAMSys/simulator/main.cpp | 2 +- .../businessObjects/phases/phase.h | 29 ++ .../businessObjects/phases/phasefactory.cpp | 6 + DRAMSys/traceAnalyzer/data/tracedb.cpp | 36 +++ DRAMSys/traceAnalyzer/data/tracedb.h | 3 + .../presentation/tracenavigator.cpp | 38 +++ .../presentation/tracenavigator.h | 3 + .../traceAnalyzer/presentation/traceplot.cpp | 35 ++- .../traceAnalyzer/presentation/traceplot.h | 6 + README.md | 150 +++++++++- 67 files changed, 2296 insertions(+), 101 deletions(-) delete mode 100644 DRAMSys/docs/flexible-refresh.pdf create mode 100644 DRAMSys/library/resources/configs/amconfigs/orgr_ddr4_4x16Gbx16_dimm_p2KB_brc.xml create mode 100644 DRAMSys/library/resources/configs/amconfigs/rgram.xml create mode 100644 DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_bw_buffer16.xml create mode 100644 DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_bw_buffer16_close_page.xml create mode 100644 DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_nbw_buffer16.xml create mode 100644 DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_nbw_buffer16_close_page.xml create mode 100644 DRAMSys/library/resources/configs/mcconfigs/rgrmccfg.xml create mode 100644 DRAMSys/library/resources/configs/memspecs/orgr_16Gb_ddr4.xml create mode 100644 DRAMSys/library/resources/configs/memspecs/orgr_16Gb_ddr4_2x.xml create mode 100644 DRAMSys/library/resources/configs/memspecs/orgr_16Gb_ddr4_4x.xml create mode 100644 DRAMSys/library/resources/configs/memspecs/rgrspec.xml create mode 100644 DRAMSys/library/resources/configs/simulator/orgr.xml create mode 100644 DRAMSys/library/resources/configs/simulator/orgr_4b_opt_timings_ddr3.xml create mode 100644 DRAMSys/library/resources/configs/simulator/orgr_4b_std_timings_ddr3.xml create mode 100644 DRAMSys/library/resources/configs/simulator/orgr_8b_opt_timings_ddr3.xml create mode 100644 DRAMSys/library/resources/configs/simulator/orgr_8b_std_timings_ddr3.xml create mode 100644 DRAMSys/library/resources/configs/simulator/orgr_ddr4.xml create mode 100644 DRAMSys/library/resources/configs/simulator/rgrsimcfg.xml create mode 100644 DRAMSys/library/resources/simulations/rgrsim.xml create mode 100644 DRAMSys/library/src/controller/core/refresh/RGR.cpp create mode 100644 DRAMSys/library/src/controller/core/refresh/RGR.h create mode 100644 DRAMSys/library/src/controller/core/scheduling/checker/ActBChecker.cpp create mode 100644 DRAMSys/library/src/controller/core/scheduling/checker/ActBChecker.h create mode 100644 DRAMSys/library/src/controller/core/scheduling/checker/PreBChecker.cpp create mode 100644 DRAMSys/library/src/controller/core/scheduling/checker/PreBChecker.h diff --git a/DRAMSys/docs/flexible-refresh.pdf b/DRAMSys/docs/flexible-refresh.pdf deleted file mode 100644 index 4ebfe2ddd04bd430464401706e3d92e357e444b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 113272 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'jUg0y&%-bBGdCWXem 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 3 bit + Rows (R): 128K [13:29] (A0 - A16) -> 17 bit + Cols (C): 1K [3:12] (A0 - A9) -> 10 bit + Byte Offset (Y): 8 [0:2] (8-byte-wide memory module, i.e., 64-bit-wide data bus) -> 3 bit + + 3 3 3 | 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 | 1 1 1 + 2 1 0 | 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 | 2 1 0 9 8 7 6 5 4 3 | 2 1 0 + B B B | R R R R R R R R R R R R R R R R R | C C C C C C C C C C | Y Y Y +--> + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/amconfigs/rgram.xml b/DRAMSys/library/resources/configs/amconfigs/rgram.xml new file mode 100644 index 00000000..b5e85906 --- /dev/null +++ b/DRAMSys/library/resources/configs/amconfigs/rgram.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_bw_buffer16.xml b/DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_bw_buffer16.xml new file mode 100644 index 00000000..66a560b4 --- /dev/null +++ b/DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_bw_buffer16.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_bw_buffer16_close_page.xml b/DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_bw_buffer16_close_page.xml new file mode 100644 index 00000000..936a8503 --- /dev/null +++ b/DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_bw_buffer16_close_page.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_nbw_buffer16.xml b/DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_nbw_buffer16.xml new file mode 100644 index 00000000..ba32827b --- /dev/null +++ b/DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_nbw_buffer16.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_nbw_buffer16_close_page.xml b/DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_nbw_buffer16_close_page.xml new file mode 100644 index 00000000..3e3d5aac --- /dev/null +++ b/DRAMSys/library/resources/configs/mcconfigs/fr_fcfs_nbw_buffer16_close_page.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/mcconfigs/rgrmccfg.xml b/DRAMSys/library/resources/configs/mcconfigs/rgrmccfg.xml new file mode 100644 index 00000000..f6b02b27 --- /dev/null +++ b/DRAMSys/library/resources/configs/mcconfigs/rgrmccfg.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/memspecs/SAMSUNG_K4B4G1646Q_4Gb_DDR3-1066_16bit.xml b/DRAMSys/library/resources/configs/memspecs/SAMSUNG_K4B4G1646Q_4Gb_DDR3-1066_16bit.xml index c8c12b52..e4c571e3 100644 --- a/DRAMSys/library/resources/configs/memspecs/SAMSUNG_K4B4G1646Q_4Gb_DDR3-1066_16bit.xml +++ b/DRAMSys/library/resources/configs/memspecs/SAMSUNG_K4B4G1646Q_4Gb_DDR3-1066_16bit.xml @@ -11,7 +11,7 @@ Device mounted: K4B4G1646Q-HYK0 Original fck is 800 MHz (DDR3-1600). Adapted to fck 533 MHz (DDR-1066). - Deepak provided most of the timing and current values. + Deepak provided most of the timing and current values. For the ones not provided datasheet values were used. --> @@ -43,25 +43,25 @@ - - + + - - + + - + - - - - - + + + + + diff --git a/DRAMSys/library/resources/configs/memspecs/orgr_16Gb_ddr4.xml b/DRAMSys/library/resources/configs/memspecs/orgr_16Gb_ddr4.xml new file mode 100644 index 00000000..42105d48 --- /dev/null +++ b/DRAMSys/library/resources/configs/memspecs/orgr_16Gb_ddr4.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/memspecs/orgr_16Gb_ddr4_2x.xml b/DRAMSys/library/resources/configs/memspecs/orgr_16Gb_ddr4_2x.xml new file mode 100644 index 00000000..a41e1e57 --- /dev/null +++ b/DRAMSys/library/resources/configs/memspecs/orgr_16Gb_ddr4_2x.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/memspecs/orgr_16Gb_ddr4_4x.xml b/DRAMSys/library/resources/configs/memspecs/orgr_16Gb_ddr4_4x.xml new file mode 100644 index 00000000..2b56ba2d --- /dev/null +++ b/DRAMSys/library/resources/configs/memspecs/orgr_16Gb_ddr4_4x.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/memspecs/rgrspec.xml b/DRAMSys/library/resources/configs/memspecs/rgrspec.xml new file mode 100644 index 00000000..d3bc6990 --- /dev/null +++ b/DRAMSys/library/resources/configs/memspecs/rgrspec.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/simulator/orgr.xml b/DRAMSys/library/resources/configs/simulator/orgr.xml new file mode 100644 index 00000000..5ea7d678 --- /dev/null +++ b/DRAMSys/library/resources/configs/simulator/orgr.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/simulator/orgr_4b_opt_timings_ddr3.xml b/DRAMSys/library/resources/configs/simulator/orgr_4b_opt_timings_ddr3.xml new file mode 100644 index 00000000..c1ee0c9b --- /dev/null +++ b/DRAMSys/library/resources/configs/simulator/orgr_4b_opt_timings_ddr3.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/simulator/orgr_4b_std_timings_ddr3.xml b/DRAMSys/library/resources/configs/simulator/orgr_4b_std_timings_ddr3.xml new file mode 100644 index 00000000..66460ef1 --- /dev/null +++ b/DRAMSys/library/resources/configs/simulator/orgr_4b_std_timings_ddr3.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/simulator/orgr_8b_opt_timings_ddr3.xml b/DRAMSys/library/resources/configs/simulator/orgr_8b_opt_timings_ddr3.xml new file mode 100644 index 00000000..10673780 --- /dev/null +++ b/DRAMSys/library/resources/configs/simulator/orgr_8b_opt_timings_ddr3.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/simulator/orgr_8b_std_timings_ddr3.xml b/DRAMSys/library/resources/configs/simulator/orgr_8b_std_timings_ddr3.xml new file mode 100644 index 00000000..d8846262 --- /dev/null +++ b/DRAMSys/library/resources/configs/simulator/orgr_8b_std_timings_ddr3.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/simulator/orgr_ddr4.xml b/DRAMSys/library/resources/configs/simulator/orgr_ddr4.xml new file mode 100644 index 00000000..4aeea8c9 --- /dev/null +++ b/DRAMSys/library/resources/configs/simulator/orgr_ddr4.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/configs/simulator/rgrsimcfg.xml b/DRAMSys/library/resources/configs/simulator/rgrsimcfg.xml new file mode 100644 index 00000000..633da858 --- /dev/null +++ b/DRAMSys/library/resources/configs/simulator/rgrsimcfg.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/DRAMSys/library/resources/resources.pri b/DRAMSys/library/resources/resources.pri index bfd194b2..81a9c654 100644 --- a/DRAMSys/library/resources/resources.pri +++ b/DRAMSys/library/resources/resources.pri @@ -4,11 +4,58 @@ # Simulation Files OTHER_FILES += resources/simulations/ddr3-example.xml OTHER_FILES += resources/simulations/ddr3-single-device.xml +OTHER_FILES += resources/simulations/ddr3-rgr.xml +OTHER_FILES += resources/simulations/ddr3-rgr00.xml +OTHER_FILES += resources/simulations/ddr3-rgr01.xml +OTHER_FILES += resources/simulations/ddr3-rgr02.xml +OTHER_FILES += resources/simulations/ddr3-rgr03.xml +OTHER_FILES += resources/simulations/ddr3-rgr04.xml +OTHER_FILES += resources/simulations/ddr3-rgr05.xml +OTHER_FILES += resources/simulations/ddr3-rgr06.xml +OTHER_FILES += resources/simulations/ddr3-rgr07.xml +OTHER_FILES += resources/simulations/ddr3-rgr08.xml +OTHER_FILES += resources/simulations/ddr3-rgr09.xml +OTHER_FILES += resources/simulations/ddr3-rgr10.xml +OTHER_FILES += resources/simulations/ddr3-rgr11.xml +OTHER_FILES += resources/simulations/ddr3-rgr12.xml +OTHER_FILES += resources/simulations/ddr3-rgr13.xml +OTHER_FILES += resources/simulations/ddr3-rgr14.xml +OTHER_FILES += resources/simulations/ddr3-rgr15.xml +OTHER_FILES += resources/simulations/ddr3-rgr16.xml +OTHER_FILES += resources/simulations/ddr3-rgr17.xml +OTHER_FILES += resources/simulations/ddr3-rgr18.xml +OTHER_FILES += resources/simulations/ddr3-rgr19.xml +OTHER_FILES += resources/simulations/ddr3-rgr20.xml +OTHER_FILES += resources/simulations/ddr3-rgr21.xml +OTHER_FILES += resources/simulations/ddr3-rgr22.xml +OTHER_FILES += resources/simulations/ddr3-rgr23.xml +OTHER_FILES += resources/simulations/ddr3-rgr24.xml +OTHER_FILES += resources/simulations/ddr3-rgr25.xml +OTHER_FILES += resources/simulations/ddr3-rgr26.xml +OTHER_FILES += resources/simulations/ddr3-rgr27.xml +OTHER_FILES += resources/simulations/ddr3-rgr28.xml +OTHER_FILES += resources/simulations/ddr3-rgr29.xml +OTHER_FILES += resources/simulations/ddr3-rgr30.xml +OTHER_FILES += resources/simulations/ddr3-rgr31.xml +OTHER_FILES += resources/simulations/ddr3-rgr32.xml +OTHER_FILES += resources/simulations/ddr3-rgr33.xml +OTHER_FILES += resources/simulations/ddr3-rgr34.xml +OTHER_FILES += resources/simulations/ddr3-rgr35.xml +OTHER_FILES += resources/simulations/ddr3-rgr36.xml +OTHER_FILES += resources/simulations/ddr3-rgr37.xml +OTHER_FILES += resources/simulations/ddr3-rgr38.xml +OTHER_FILES += resources/simulations/ddr3-rgr39.xml +OTHER_FILES += resources/simulations/ddr3-rgr40.xml +OTHER_FILES += resources/simulations/ddr3-rgr41.xml +OTHER_FILES += resources/simulations/ddr3-rgr42.xml +OTHER_FILES += resources/simulations/ddr3-rgr43.xml +OTHER_FILES += resources/simulations/ddr3-rgr44.xml OTHER_FILES += resources/simulations/wideio-example.xml OTHER_FILES += resources/simulations/wideio-ecc.xml OTHER_FILES += resources/simulations/ddr3-ecc.xml OTHER_FILES += resources/simulations/sms-example.xml OTHER_FILES += resources/simulations/ddr3_postpone_ref_test.xml +OTHER_FILES += resources/simulations/rgrsim.xml # Simulator Files OTHER_FILES += resources/configs/simulator/wideio.xml @@ -18,6 +65,7 @@ OTHER_FILES += resources/configs/simulator/wideio_thermal.xml OTHER_FILES += resources/configs/simulator/wideio_ecc.xml OTHER_FILES += resources/configs/simulator/ddr3_ecc.xml OTHER_FILES += resources/configs/simulator/sms.xml +OTHER_FILES += resources/configs/simulator/rgrsimcfg.xml # Scripts OTHER_FILES += resources/scripts/address_scrambler.pl @@ -69,6 +117,8 @@ OTHER_FILES += resources/traces/small.stl OTHER_FILES += resources/traces/chstone-motion_32.stl OTHER_FILES += resources/traces/mediabench-adpcmdecode_32.stl OTHER_FILES += resources/traces/ddr3_example.stl +OTHER_FILES += resources/traces/ddr3_exampleb.stl +OTHER_FILES += resources/traces/ddr3_rgr.stl OTHER_FILES += resources/traces/ddr3_single_dev_example.stl OTHER_FILES += resources/traces/ddr3_SAMSUNG_M471B5674QH0_DIMM_example.stl OTHER_FILES += resources/traces/test_ecc.stl @@ -88,6 +138,7 @@ OTHER_FILES += resources/configs/mcconfigs/fr_fcfs.xml OTHER_FILES += resources/configs/mcconfigs/par_bs.xml OTHER_FILES += resources/configs/mcconfigs/fifo_ecc.xml OTHER_FILES += resources/configs/mcconfigs/sms.xml +OTHER_FILES += resources/configs/mcconfigs/rgrmccfg.xml # Memspecs OTHER_FILES += resources/configs/memspecs/memspec.dtd @@ -129,9 +180,11 @@ OTHER_FILES += resources/configs/memspecs/MICRON_4Gb_LPDDR3-1333_32bit_A.xml OTHER_FILES += resources/configs/memspecs/MICRON_4Gb_LPDDR3-1600_32bit_A.xml OTHER_FILES += resources/configs/memspecs/SAMSUNG_K4B1G1646E_1Gb_DDR3-1600_16bit.xml OTHER_FILES += resources/configs/memspecs/SAMSUNG_K4B4G1646Q_4Gb_DDR3-1066_16bit.xml +OTHER_FILES += resources/configs/memspecs/orgr_16Gb_ddr4.xml OTHER_FILES += resources/configs/memspecs/wideio.xml OTHER_FILES += resources/configs/memspecs/wideio_less_refresh.xml OTHER_FILES += resources/configs/memspecs/MICRON_1Gb_DDR3-1600_8bit_G_less_refresh.xml +OTHER_FILES += resources/configs/memspecs/rgrspec.xml # Address Mapping Configs OTHER_FILES += resources/configs/amconfigs/am_ddr3.xml @@ -151,6 +204,7 @@ OTHER_FILES += resources/configs/amconfigs/am_wideioFourBanks.xml OTHER_FILES += resources/configs/amconfigs/am_ddr3_1Gbx8_p1KB_brc.xml OTHER_FILES += resources/configs/amconfigs/am_ddr3_4x4Gbx16_dimm_p2KB_brc.xml OTHER_FILES += resources/configs/amconfigs/am_ddr3_4x4Gbx16_dimm_p2KB_rbc.xml +OTHER_FILES += resources/configs/amconfigs/rgram.xml # Thermal Simulation configs OTHER_FILES += resources/configs/thermalsim/core.flp diff --git a/DRAMSys/library/resources/simulations/rgrsim.xml b/DRAMSys/library/resources/simulations/rgrsim.xml new file mode 100644 index 00000000..fa6201b4 --- /dev/null +++ b/DRAMSys/library/resources/simulations/rgrsim.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + rgr_postpone_test.stl + + diff --git a/DRAMSys/library/src/common/TlmRecorder.cpp b/DRAMSys/library/src/common/TlmRecorder.cpp index b75141a6..9cc03443 100644 --- a/DRAMSys/library/src/common/TlmRecorder.cpp +++ b/DRAMSys/library/src/common/TlmRecorder.cpp @@ -252,6 +252,10 @@ void TlmRecorder::setUpTransactionTerminatingPhases() // Refresh Bank transactionTerminatingPhases.push_back(static_cast (END_REFB)); + transactionTerminatingPhases.push_back(static_cast + (END_ACTB)); + transactionTerminatingPhases.push_back(static_cast + (END_PREB)); // Phases for Power Down transactionTerminatingPhases.push_back(static_cast diff --git a/DRAMSys/library/src/common/protocol.h b/DRAMSys/library/src/common/protocol.h index eb9d005b..00b03c36 100644 --- a/DRAMSys/library/src/common/protocol.h +++ b/DRAMSys/library/src/common/protocol.h @@ -39,12 +39,18 @@ #define EXTENDED_PHASE_DRAM // DRAM Control Phases +DECLARE_EXTENDED_PHASE(BEGIN_PREB); +DECLARE_EXTENDED_PHASE(END_PREB); + DECLARE_EXTENDED_PHASE(BEGIN_PRE); DECLARE_EXTENDED_PHASE(END_PRE); DECLARE_EXTENDED_PHASE(BEGIN_PRE_ALL); DECLARE_EXTENDED_PHASE(END_PRE_ALL); +DECLARE_EXTENDED_PHASE(BEGIN_ACTB); +DECLARE_EXTENDED_PHASE(END_ACTB); + DECLARE_EXTENDED_PHASE(BEGIN_ACT); DECLARE_EXTENDED_PHASE(END_ACT); diff --git a/DRAMSys/library/src/common/third_party/DRAMPower b/DRAMSys/library/src/common/third_party/DRAMPower index 90d6290f..fa0627c4 160000 --- a/DRAMSys/library/src/common/third_party/DRAMPower +++ b/DRAMSys/library/src/common/third_party/DRAMPower @@ -1 +1 @@ -Subproject commit 90d6290f802c29b3de9e10233ceee22290907ce6 +Subproject commit fa0627c4e6c5b35a040e416592061fb7e672daaf diff --git a/DRAMSys/library/src/controller/Command.cpp b/DRAMSys/library/src/controller/Command.cpp index 448941b9..7b1b9d9f 100644 --- a/DRAMSys/library/src/controller/Command.cpp +++ b/DRAMSys/library/src/controller/Command.cpp @@ -54,9 +54,15 @@ std::string commandToString(Command command) case Command::WriteA: return "WRA"; break; + case Command::PreB: + return "PREB"; + break; case Command::Precharge: return "PRE"; break; + case Command::ActB: + return "ACTB"; + break; case Command::Activate: return "ACT"; break; @@ -100,10 +106,22 @@ std::string commandToString(Command command) const std::vector &getAllCommands() { - static std::vector allCommands( { Command::Precharge, Command::PrechargeAll, - Command::Activate, Command::Read, Command::Write, Command::ReadA, Command::WriteA, - Command::AutoRefresh, Command::PDNA, Command::PDNAX, Command::PDNP, Command::PDNPX, - Command::SREF, Command::SREFX + static std::vector allCommands( { Command::PreB, + Command::Precharge, + Command::PrechargeAll, + Command::ActB, + Command::Activate, + Command::Read, + Command::Write, + Command::ReadA, + Command::WriteA, + Command::AutoRefresh, + Command::PDNA, + Command::PDNAX, + Command::PDNP, + Command::PDNPX, + Command::SREF, + Command::SREFX }); return allCommands; } diff --git a/DRAMSys/library/src/controller/Command.h b/DRAMSys/library/src/controller/Command.h index e8b5ee3f..da66fef4 100644 --- a/DRAMSys/library/src/controller/Command.h +++ b/DRAMSys/library/src/controller/Command.h @@ -40,7 +40,26 @@ #include -enum class Command {NOP, Precharge, PrechargeAll, Activate, Read, Write, ReadA, WriteA, AutoRefresh, PDNA, PDNAX, PDNP, PDNPX, SREF, SREFX}; +enum class Command { + NOP, + PreB, + Precharge, + PrechargeAll, + ActB, + Activate, + Read, + Write, + ReadA, + WriteA, + AutoRefresh, + PDNA, + PDNAX, + PDNP, + PDNPX, + SREF, + SREFX +}; + std::string commandToString(Command command); const std::vector &getAllCommands(); bool commandIsIn(Command command, std::vector commands); diff --git a/DRAMSys/library/src/controller/Controller.cpp b/DRAMSys/library/src/controller/Controller.cpp index 3d0e6c58..e7bb8af2 100644 --- a/DRAMSys/library/src/controller/Controller.cpp +++ b/DRAMSys/library/src/controller/Controller.cpp @@ -35,13 +35,13 @@ * Felipe S. Prado */ +#include "core/configuration/Configuration.h" #include "Controller.h" #include void Controller::buildScheduler() { string selectedScheduler = Configuration::getInstance().Scheduler; - std::cout << "Selected Scheduler: " << selectedScheduler << std::endl; if (selectedScheduler == "FIFO") { scheduler = new Fifo(*controllerCore); @@ -111,10 +111,18 @@ void Controller::send(const ScheduledCommand &command, controllerCorePEQ.notify(payload, BEGIN_REFB, command.getStart() - sc_time_stamp()); break; + case Command::ActB: + controllerCorePEQ.notify(payload, BEGIN_ACTB, + command.getStart() - sc_time_stamp()); + break; case Command::Activate: controllerCorePEQ.notify(payload, BEGIN_ACT, command.getStart() - sc_time_stamp()); break; + case Command::PreB: + controllerCorePEQ.notify(payload, BEGIN_PREB, + command.getStart() - sc_time_stamp()); + break; case Command::Precharge: controllerCorePEQ.notify(payload, BEGIN_PRE, command.getStart() - sc_time_stamp()); @@ -223,7 +231,9 @@ void Controller::controllerCorePEQCallback(tlm_generic_payload &payload, else if (containsPhase(phase, { END_PDNA, END_PDNP, END_SREF })) printDebugMessage("Leaving PowerDown " + phaseNameToString( phase) + " on all banks" ); - else if (containsPhase(phase, { BEGIN_RD, BEGIN_WR, BEGIN_ACT, BEGIN_PRE, BEGIN_PRE_ALL, BEGIN_RDA, BEGIN_WRA })) { + else if (containsPhase(phase, { BEGIN_RD, BEGIN_WR, BEGIN_ACTB, BEGIN_ACT, BEGIN_PREB, BEGIN_PRE, BEGIN_PRE_ALL, BEGIN_RDA, BEGIN_WRA })) { + printDebugMessage("Controller has sent to DRAM this: " + phaseNameToString( + phase) + " bank " + to_string(bank.ID())); } else SC_REPORT_FATAL(0, @@ -492,7 +502,7 @@ void Controller::dramPEQCallback(tlm_generic_payload &payload, scheduleNextFromScheduler(bank); } scheduleNextFromScheduler(bank); - } else if (containsPhase(phase, { END_PRE, END_ACT })) { + } else if (containsPhase(phase, {END_PREB, END_PRE, END_ACTB, END_ACT})) { scheduleNextFromScheduler(bank); } else if (phase == END_PRE_ALL) { diff --git a/DRAMSys/library/src/controller/ControllerState.cpp b/DRAMSys/library/src/controller/ControllerState.cpp index 14e19b23..efff0548 100644 --- a/DRAMSys/library/src/controller/ControllerState.cpp +++ b/DRAMSys/library/src/controller/ControllerState.cpp @@ -121,11 +121,19 @@ void ControllerState::change(const ScheduledCommand &scheduledCommand) break; case Command::AutoRefresh: break; + case Command::ActB: + rowBufferStates->openRowInRowBuffer(scheduledCommand.getBank(), + scheduledCommand.getRow()); + lastActivatesB.emplace(scheduledCommand.getStart(), scheduledCommand); + break; case Command::Activate: rowBufferStates->openRowInRowBuffer(scheduledCommand.getBank(), scheduledCommand.getRow()); lastActivates.emplace(scheduledCommand.getStart(), scheduledCommand); break; + case Command::PreB: + rowBufferStates->closeRowBuffer(scheduledCommand.getBank()); + break; case Command::Precharge: rowBufferStates->closeRowBuffer(scheduledCommand.getBank()); break; @@ -153,6 +161,10 @@ void ControllerState::cleanUp(sc_time time) if (time >= config->memSpec.tActHistory()) lastActivates.erase(lastActivates.begin(), lastActivates.lower_bound(time - config->memSpec.tActHistory())); + + if (time >= config->memSpec.tActBHistory()) + lastActivatesB.erase(lastActivatesB.begin(), + lastActivatesB.lower_bound(time - config->memSpec.tActBHistory())); } void ControllerState::printDebugMessage(std::string message) diff --git a/DRAMSys/library/src/controller/ControllerState.h b/DRAMSys/library/src/controller/ControllerState.h index f4eeb36c..fff61882 100644 --- a/DRAMSys/library/src/controller/ControllerState.h +++ b/DRAMSys/library/src/controller/ControllerState.h @@ -76,6 +76,7 @@ public: Slots bus; std::vector lastDataStrobeCommands; std::map lastActivates; + std::map lastActivatesB; private: std::string ownerName; diff --git a/DRAMSys/library/src/controller/core/ControllerCore.cpp b/DRAMSys/library/src/controller/core/ControllerCore.cpp index eb9c5914..0a9f434a 100644 --- a/DRAMSys/library/src/controller/core/ControllerCore.cpp +++ b/DRAMSys/library/src/controller/core/ControllerCore.cpp @@ -37,7 +37,9 @@ #include #include "ControllerCore.h" +#include "scheduling/checker/ActBChecker.h" #include "scheduling/checker/ActivateChecker.h" +#include "scheduling/checker/PreBChecker.h" #include "scheduling/checker/PrechargeChecker.h" #include "scheduling/checker/PrechargeAllChecker.h" #include "scheduling/checker/ReadChecker.h" @@ -46,6 +48,7 @@ #include "scheduling/checker/PowerDownChecker.h" #include "refresh/RefreshManagerBankwise.h" #include "refresh/RefreshManager.h" +#include "refresh/RGR.h" #include "../../common/dramExtension.h" #include "../../common/Utils.h" #include "TimingCalculation.h" @@ -64,7 +67,9 @@ ControllerCore::ControllerCore(sc_module_name /*name*/, { state = new ControllerState(name(), &config); + commandChecker[Command::ActB] = new ActBChecker(config, *state); commandChecker[Command::Activate] = new ActivateChecker(config, *state); + commandChecker[Command::PreB] = new PreBChecker(config, *state); commandChecker[Command::Precharge] = new PrechargeChecker(config, *state); commandChecker[Command::PrechargeAll] = new PrechargeAllChecker(config, *state); commandChecker[Command::Read] = new ReadChecker(config, *state); @@ -80,10 +85,24 @@ ControllerCore::ControllerCore(sc_module_name /*name*/, commandChecker[Command::PDNPX] = commandChecker[Command::PDNA]; commandChecker[Command::SREFX] = commandChecker[Command::PDNA]; - if (config.BankwiseLogic) { - refreshManager = new RefreshManagerBankwise("refManagerBw", *this); + if (config.RowGranularRef) { + refreshManager = new RGR("RGR", *this); + assert(config.getTrasb() <= config.memSpec.tRAS); + assert(config.getTrasb() >= config.memSpec.tRCD); + if (config.memSpec.tRRD_L != config.memSpec.tRRD_S) { + cout << "double check assertions" << endl; + } + assert(config.getTrrdb_L() <= config.memSpec.tRRD_L); + assert(config.getTrrdb_S() <= config.memSpec.tRRD_S); + assert(config.getTrpb() <= config.memSpec.tRP); + assert(config.getTrcb() <= config.memSpec.tRC); + assert(config.getTfawb() <= config.memSpec.tNAW); } else { - refreshManager = new RefreshManager("refManager", *this); + if (config.BankwiseLogic) { + refreshManager = new RefreshManagerBankwise("refManagerBw", *this); + } else { + refreshManager = new RefreshManager("refManager", *this); + } } if (config.PowerDownMode == EPowerDownMode::Staggered) { @@ -187,6 +206,10 @@ bool ControllerCore::hasPendingRequests() return false; } +bool ControllerCore::hasPendingRequests(Bank bank) +{ + return (numberOfPayloads[bank] != 0) ? true : false; +} bool ControllerCore::bankIsBusy(Bank bank) { @@ -199,7 +222,7 @@ bool ControllerCore::bankIsBusy(Bank bank) // Read and writes can overlap, so the bank should not be busy during a rd/wr return (time < lastScheduledCommand.getStart()); } - else if (lastScheduledCommand.commandIsIn( { Command::WriteA, Command::ReadA, Command::Precharge, Command::PrechargeAll, Command::Activate })) { + else if (lastScheduledCommand.commandIsIn( { Command::WriteA, Command::ReadA, Command::PreB, Command::Precharge, Command::PrechargeAll, Command::ActB, Command::Activate })) { return (time < lastScheduledCommand.getEnd()); } else if (lastScheduledCommand.getCommand() == Command::AutoRefresh) { diff --git a/DRAMSys/library/src/controller/core/ControllerCore.h b/DRAMSys/library/src/controller/core/ControllerCore.h index 405b25f6..7a3818ef 100644 --- a/DRAMSys/library/src/controller/core/ControllerCore.h +++ b/DRAMSys/library/src/controller/core/ControllerCore.h @@ -68,6 +68,7 @@ public: return *(state->rowBufferStates); } bool hasPendingRequests(); + bool hasPendingRequests(Bank bank); bool bankIsBusy(Bank bank); ICommandChecker &getCommandChecker(Command command); diff --git a/DRAMSys/library/src/controller/core/TimingCalculation.cpp b/DRAMSys/library/src/controller/core/TimingCalculation.cpp index da1db258..2c1f17cb 100644 --- a/DRAMSys/library/src/controller/core/TimingCalculation.cpp +++ b/DRAMSys/library/src/controller/core/TimingCalculation.cpp @@ -71,8 +71,12 @@ sc_time getExecutionTime(Command command, tlm::tlm_generic_payload &payload) { MemSpec &config = Configuration::getInstance().memSpec; - if (command == Command::Precharge || command == Command::PrechargeAll) { + if (command == Command::PreB) { + return Configuration::getInstance().getTrpb(); + } else if (command == Command::Precharge || command == Command::PrechargeAll) { return config.tRP; + } else if (command == Command::ActB) { + return config.tRCD; } else if (command == Command::Activate) { return config.tRCD; } else if (command == Command::Read) { diff --git a/DRAMSys/library/src/controller/core/configuration/Configuration.cpp b/DRAMSys/library/src/controller/core/configuration/Configuration.cpp index d2653b39..42f17b21 100644 --- a/DRAMSys/library/src/controller/core/configuration/Configuration.cpp +++ b/DRAMSys/library/src/controller/core/configuration/Configuration.cpp @@ -198,6 +198,39 @@ void Configuration::setParameter(std::string name, std::string value) } } else if (name == "ControllerCoreDisableRefresh") ControllerCoreDisableRefresh = string2bool(value); + else if (name == "ControllerCoreRowGranularRef") + RowGranularRef = string2bool(value); + else if (name == "ControllerCoreRowGranularRefRowInc")RowInc = string2int( + value); + else if (name == "ControllerCoreRowGranularRefNumAR")NumAR = string2int(value); + else if (name == "ControllerCoreRowGranularRefB0")RGRB0 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefB1")RGRB1 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefB2")RGRB2 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefB3")RGRB3 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefB4")RGRB4 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefB5")RGRB5 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefB6")RGRB6 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefB7")RGRB7 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefB8")RGRB8 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefB9")RGRB9 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefB10")RGRB10 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefB11")RGRB11 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefB12")RGRB12 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefB13")RGRB13 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefB14")RGRB14 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefB15")RGRB15 = string2bool(value); + else if (name == "ControllerCoreRowGranularRefRASBInClkCycles") + trasbclk = string2int(value); + else if (name == "ControllerCoreRowGranularRefRRDB_LInClkCycles") + trrdblclk = string2int(value); + else if (name == "ControllerCoreRowGranularRefRRDB_SInClkCycles") + trrdbsclk = string2int(value); + else if (name == "ControllerCoreRowGranularRefRPBInClkCycles") + trpbclk = string2int(value); + else if (name == "ControllerCoreRowGranularRefRCBInClkCycles") + trcbclk = string2int(value); + else if (name == "ControllerCoreRowGranularRefFAWBInClkCycles") + tfawbclk = string2int(value); else if (name == "ControllerCoreForceMaxRefBurst") ControllerCoreForceMaxRefBurst = string2bool(value); else if (name == "ControllerCoreEnableRefPostpone") { @@ -237,7 +270,6 @@ void Configuration::setParameter(std::string name, std::string value) #else AddressOffset = 0; #endif - cout << "Address Offset: " << AddressOffset << endl; } else if (name == "CheckTLM2Protocol") CheckTLM2Protocol = string2bool(value); else if (name == "ECCControllerMode") @@ -350,6 +382,47 @@ unsigned int Configuration::getBytesPerBurst() return bytesPerBurst; } +sc_time Configuration::getTrasb() +{ + return trasbclk * memSpec.clk; +} +sc_time Configuration::getTrrdb_L() +{ + return trrdblclk * memSpec.clk; +} +sc_time Configuration::getTrrdb_S() +{ + return trrdbsclk * memSpec.clk; +} +sc_time Configuration::getTrpb() +{ + return trpbclk * memSpec.clk; +} +sc_time Configuration::getTrcb() +{ + return trcbclk * memSpec.clk; +} +sc_time Configuration::getTfawb() +{ + return tfawbclk * memSpec.clk; +} +bool Configuration::getRGRBank(unsigned int w) +{ + bool flg = w == 0 ? RGRB0 : w == 1 ? RGRB1 : w == 2 ? RGRB2 : w == 3 ? RGRB3 : w + == 4 ? RGRB4 : w == 5 ? RGRB5 : w == 6 ? RGRB6 : w == 7 ? RGRB7 : w == 8 ? RGRB8 + : w == 9 ? RGRB9 : w == 10 ? RGRB10 : w == 11 ? RGRB11 : w == 12 ? RGRB12 : w == + 13 ? RGRB13 : w == 14 ? RGRB14 : w == 15 ? RGRB15 : true; + return flg; +} +unsigned int Configuration::getNumAR(void) +{ + return NumAR; +} +unsigned int Configuration::getRowInc(void) +{ + return RowInc; +} + // Changes the number of bytes depeding on the ECC Controller. This function is needed for modules which get data directly or indirectly from the ECC Controller unsigned int Configuration::adjustNumBytesAfterECC(unsigned nBytes) { diff --git a/DRAMSys/library/src/controller/core/configuration/Configuration.h b/DRAMSys/library/src/controller/core/configuration/Configuration.h index ea179604..99c9cb9e 100644 --- a/DRAMSys/library/src/controller/core/configuration/Configuration.h +++ b/DRAMSys/library/src/controller/core/configuration/Configuration.h @@ -41,6 +41,7 @@ #include #include +#include #include "MemSpec.h" #include "thermalSimConfig.h" #include "../../../common/Utils.h" @@ -85,6 +86,40 @@ struct Configuration { bool Debug = false; unsigned int NumberOfMemChannels = 1; bool ControllerCoreDisableRefresh = false; + bool RowGranularRef = false; + unsigned int trasbclk = 0; + sc_time getTrasb(); + unsigned int trrdblclk = 0; + sc_time getTrrdb_L(); + unsigned int trrdbsclk = 0; + sc_time getTrrdb_S(); + unsigned int trpbclk = 0; + sc_time getTrpb(); + unsigned int trcbclk = 0; + sc_time getTrcb(); + unsigned int tfawbclk = 0; + sc_time getTfawb(); + bool RGRB0 = true; + bool RGRB1 = true; + bool RGRB2 = true; + bool RGRB3 = true; + bool RGRB4 = true; + bool RGRB5 = true; + bool RGRB6 = true; + bool RGRB7 = true; + bool RGRB8 = true; + bool RGRB9 = true; + bool RGRB10 = true; + bool RGRB11 = true; + bool RGRB12 = true; + bool RGRB13 = true; + bool RGRB14 = true; + bool RGRB15 = true; + unsigned int NumAR = 8192; + unsigned int RowInc = 1; + bool getRGRBank(unsigned int); + unsigned int getNumAR(void); + unsigned int getRowInc(void); bool ControllerCoreForceMaxRefBurst = false; bool ControllerCoreEnableRefPostpone = false; bool ControllerCoreEnableRefPullIn = false; diff --git a/DRAMSys/library/src/controller/core/configuration/ConfigurationLoader.cpp b/DRAMSys/library/src/controller/core/configuration/ConfigurationLoader.cpp index 8da5a85a..75049f56 100644 --- a/DRAMSys/library/src/controller/core/configuration/ConfigurationLoader.cpp +++ b/DRAMSys/library/src/controller/core/configuration/ConfigurationLoader.cpp @@ -116,8 +116,6 @@ void ConfigurationLoader::loadMemSpec(Configuration &config, config.memSpec.MemoryId = queryStringParameter(memspec, "memoryId"); config.memSpec.MemoryType = queryStringParameter(memspec, "memoryType"); - std::cout << "Memtype: " << config.memSpec.MemoryType << std::endl; - if (config.memSpec.MemoryType == "DDR4") { loadDDR4(config, memspec); } else if (config.memSpec.MemoryType == "DDR3") { diff --git a/DRAMSys/library/src/controller/core/configuration/MemSpec.h b/DRAMSys/library/src/controller/core/configuration/MemSpec.h index b954e4b2..24a0b852 100644 --- a/DRAMSys/library/src/controller/core/configuration/MemSpec.h +++ b/DRAMSys/library/src/controller/core/configuration/MemSpec.h @@ -147,6 +147,10 @@ struct MemSpec { { return tNAW; } + sc_time tActBHistory() + { + return tNAW; + } sc_time tDataStrobeHistory() { return tWTR_L; diff --git a/DRAMSys/library/src/controller/core/refresh/IRefreshManager.h b/DRAMSys/library/src/controller/core/refresh/IRefreshManager.h index 5ab46989..1668ef61 100644 --- a/DRAMSys/library/src/controller/core/refresh/IRefreshManager.h +++ b/DRAMSys/library/src/controller/core/refresh/IRefreshManager.h @@ -41,6 +41,15 @@ #include #include "../scheduling/ScheduledCommand.h" +// Flex. refresh (pull-in, postpone) +typedef enum { + ST_REFRESH = 0, + ST_PULLIN, + ST_POSTPONE, + ST_SKIP, + ST_BURST, + ST_ALIGN +} ref_fsm_state_t; class IRefreshManager { @@ -50,10 +59,8 @@ public: virtual void scheduleRefresh(tlm::tlm_generic_payload &payload, sc_time time) = 0; virtual void reInitialize(Bank bank, sc_time time) = 0; - - virtual bool isInvalidated(tlm::tlm_generic_payload &payload, sc_time time) = 0; }; +#endif /* IREFRESHMANAGER_H_ */ -#endif diff --git a/DRAMSys/library/src/controller/core/refresh/RGR.cpp b/DRAMSys/library/src/controller/core/refresh/RGR.cpp new file mode 100644 index 00000000..3be8eeee --- /dev/null +++ b/DRAMSys/library/src/controller/core/refresh/RGR.cpp @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2017, University of Kaiserslautern + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Éder F. Zulian + */ + +#include "RGR.h" +#include "../ControllerCore.h" +#include "../TimingCalculation.h" +#include "../../../common/Utils.h" + +using namespace std; + +RGR::RGR(sc_module_name, ControllerCore &ctrlcore) : ccore(ctrlcore), + timing(ctrlcore.config.memSpec.refreshTimings[ccore.getBanks()[0]]) +{ + if (ccore.config.ControllerCoreEnableRefPostpone) { + maxpostpone = ccore.config.ControllerCoreMaxPostponedARCmd; + } + if (ccore.config.ControllerCoreEnableRefPullIn) { + maxpullin = ccore.config.ControllerCoreMaxPulledInARCmd; + } +#if 0 + if (Configuration::getInstance().BankwiseLogic) { + for (Bank b : ccore.getBanks()) { + sc_time refi = Configuration::getInstance().memSpec.refreshTimings[b].tREFI; + nextPlannedRefreshs[b] = b.ID() * refi / + Configuration::getInstance().memSpec.NumberOfBanks; + } + } +#endif + for (Bank b : ccore.getBanks()) { + nextPlannedRefreshs[b] = SC_ZERO_TIME; + arCmdCounter[b] = 0; + alignValue[b] = 0; + currentState[b] = ST_REFRESH; + previousState[b] = ST_REFRESH; + nextState[b] = ST_REFRESH; + setUpDummy(rps[b], b); + } + if (Configuration::getInstance().BankwiseLogic) { + for (Bank b : ccore.getBanks()) { + planNextRefresh(b, timing.tREFI); + } + } else { + planNextRefresh(ccore.getBanks()[0], timing.tREFI); + } +} + +RGR::~RGR() +{ +} + +bool RGR::hasCollision(const ScheduledCommand __attribute__((unused)) &cmd) +{ +#if 0 + bool r = false; + nbs = Configuration::getInstance().memSpec.NumberOfBanks; + for (unsigned b = 0; b < nbs; b++) { + if (cmd.getStart() < currentRefresh[b] && cmd.getEnd() > currentRefresh[b]) + r = true; + + if (cmd.getStart() < nextPlannedRefreshs[b] + && cmd.getEnd() > nextPlannedRefreshs[b]) + r = true; + } + + return r; +#else + return false; +#endif +} + +void RGR::doRefresh(tlm::tlm_generic_payload &p, sc_time t) +{ + sc_assert(!isInvalidated(p, t)); + auto nr = Configuration::getInstance().memSpec.NumberOfRows; + auto nar = Configuration::getInstance().getNumAR(); + auto ri = Configuration::getInstance().getRowInc(); + assert((nr / nar) > 0); + Bank b = DramExtension::getExtension(p).getBank(); + bool bwl = Configuration::getInstance().BankwiseLogic; + bool o = ccore.state->rowBufferStates->rowBufferIsOpen(b); + bool ac = ccore.state->rowBufferStates->allRowBuffersAreClosed(); + bool pre = !!(bwl ? o : (!ac)); + + if (!bwl) { + for (Bank b : ccore.getBanks()) { + currentRefresh[b] = t; + } + } else { + currentRefresh[b] = t; + } + + if (pre) { + if (!bwl) { + for (Bank b : ccore.getBanks()) { + auto rgrb = Configuration::getInstance().getRGRBank(b.ID()); + if (ccore.state->rowBufferStates->rowBufferIsOpen(b) && rgrb) { + ccore.scheduleRequest(Command::PreB, rps[Bank(b)]); + } + } + } else { + if (Configuration::getInstance().getRGRBank(b.ID())) { + ccore.scheduleRequest(Command::PreB, rps[b]); + } + } + } + + for (unsigned r = 0; r < (nr / nar); r += ri) { + if (!!bwl) { + if (Configuration::getInstance().getRGRBank(b.ID())) { + ccore.scheduleRequest(Command::ActB, rps[b]); + ccore.scheduleRequest(Command::PreB, rps[b]); + } + DramExtension::getExtension(p).incrementRow(); + } else { + for (Bank b : ccore.getBanks()) { + if (Configuration::getInstance().getRGRBank(b.ID())) { + ccore.scheduleRequest(Command::ActB, rps[b]); + ccore.scheduleRequest(Command::PreB, rps[b]); + } + DramExtension::getExtension(rps[b]).incrementRow(); + } + } + } +} + +void RGR::scheduleRefresh(tlm::tlm_generic_payload &p, sc_time t) +{ + sc_time nrt; + Bank b = DramExtension::getExtension(p).getBank(); + auto bwl = Configuration::getInstance().BankwiseLogic; + bool preq = bwl ? ccore.hasPendingRequests(b) : ccore.hasPendingRequests(); + bool canPostpone = preq && (arCmdCounter[b] < maxpostpone); + bool canPullIn = !preq && (arCmdCounter[b] < maxpullin); + bool fmb = !!ccore.config.ControllerCoreForceMaxRefBurst; + previousState[b] = currentState[b]; + currentState[b] = nextState[b]; + switch (currentState[b]) { + case ST_REFRESH: + assert(arCmdCounter[b] == 0); + if (canPostpone) { + nextState[b] = ST_POSTPONE; + nrt = SC_ZERO_TIME; + } else if (canPullIn) { + nextState[b] = ST_PULLIN; + nrt = SC_ZERO_TIME; + } else { + doRefresh(p, t); + nrt = timing.tREFI; + nextState[b] = ST_REFRESH; + } + break; + case ST_PULLIN: + if (canPullIn) { + doRefresh(p, t); + arCmdCounter[b]++; + nextState[b] = ST_PULLIN; + nrt = SC_ZERO_TIME; + } else { + alignValue[b] = arCmdCounter[b]; + nextState[b] = ST_ALIGN; + nrt = SC_ZERO_TIME; + } + break; + case ST_SKIP: + arCmdCounter[b]--; + if (arCmdCounter[b] == 0) { + nextState[b] = ST_REFRESH; + nrt = SC_ZERO_TIME; + } else { + nextState[b] = ST_SKIP; + nrt = timing.tREFI; + } + break; + case ST_POSTPONE: + if ((arCmdCounter[b] == maxpostpone) || ((!preq) && !fmb)) { + if (arCmdCounter[b] < maxpostpone) { + arCmdCounter[b]++; + } + alignValue[b] = arCmdCounter[b]; + nextState[b] = ST_BURST; + nrt = SC_ZERO_TIME; + } else { + arCmdCounter[b]++; + nextState[b] = ST_POSTPONE; + nrt = timing.tREFI; + } + break; + case ST_BURST: + arCmdCounter[b]--; + doRefresh(p, t); + if (arCmdCounter[b] == 0) { + nextState[b] = ST_ALIGN; + nrt = SC_ZERO_TIME; + } else { + nextState[b] = ST_BURST; + nrt = SC_ZERO_TIME; + } + break; + case ST_ALIGN: + if (previousState[b] == ST_PULLIN) { + nrt = timing.tREFI; + nextState[b] = ST_SKIP; + } else { + nrt = timing.tREFI; + nextState[b] = ST_REFRESH; + } + break; + default: + SC_REPORT_FATAL(this->name(), "Invalid State in Flexible Refresh FSM. Stop."); + break; + } + planNextRefresh(!bwl ? ccore.getBanks()[0] : b, nrt); +} + +void RGR::planNextRefresh(Bank b, sc_time t) +{ + nextPlannedRefreshs[b] += t; + ccore.controller.send(REFTrigger, nextPlannedRefreshs[b], rps[b]); +} + +void RGR::reInitialize(Bank b, sc_time t) +{ + nextPlannedRefreshs[b] = clkAlign(t, Alignment::DOWN); + planNextRefresh(b, timing.tREFI); +} + +bool RGR::isInvalidated(tlm::tlm_generic_payload &p, sc_time t) +{ + return nextPlannedRefreshs[DramExtension::getExtension(p).getBank()] > t; +} + +void RGR::printDebugMessage(std::string msg) +{ + DebugManager::getInstance().printDebugMessage(this->name(), msg); +} diff --git a/DRAMSys/library/src/controller/core/refresh/RGR.h b/DRAMSys/library/src/controller/core/refresh/RGR.h new file mode 100644 index 00000000..3cf31b10 --- /dev/null +++ b/DRAMSys/library/src/controller/core/refresh/RGR.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017, University of Kaiserslautern + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Éder F. Zulian + */ + +#ifndef RGR_MANAGER_H_ +#define RGR_MANAGER_H_ +#include "../../../common/dramExtension.h" +#include "../configuration/MemSpec.h" +#include "IRefreshManager.h" +class ControllerCore; +class RGR : public IRefreshManager, public sc_module +{ +public: + RGR(sc_module_name, ControllerCore &ctrlcore); + virtual ~RGR(); + virtual bool hasCollision(const ScheduledCommand __attribute__(( + unused)) &command) override; + virtual void scheduleRefresh(tlm::tlm_generic_payload &p, sc_time t) override; + void reInitialize(Bank bank, sc_time time) override; + bool isInvalidated(tlm::tlm_generic_payload &payload, sc_time time) override; +private: + ControllerCore &ccore; + RefreshTiming &timing; + std::map rps; + std::map nextPlannedRefreshs; + std::map currentRefresh; + unsigned int maxpostpone = 0; + unsigned int maxpullin = 0; + std::map arCmdCounter; + std::map alignValue; + std::map currentState; + std::map previousState; + std::map nextState; + void doRefresh(tlm::tlm_generic_payload &p, sc_time t); + void planNextRefresh(Bank b, sc_time t); + void printDebugMessage(std::string message); +}; +#endif /* RGR_MANAGER_H_ */ + diff --git a/DRAMSys/library/src/controller/core/refresh/RefreshManager.h b/DRAMSys/library/src/controller/core/refresh/RefreshManager.h index ee28c25f..2ec5792d 100644 --- a/DRAMSys/library/src/controller/core/refresh/RefreshManager.h +++ b/DRAMSys/library/src/controller/core/refresh/RefreshManager.h @@ -40,15 +40,6 @@ #include "IRefreshManager.h" #include "../configuration/MemSpec.h" -typedef enum { - ST_REFRESH = 0, - ST_PULLIN, - ST_POSTPONE, - ST_SKIP, - ST_BURST, - ST_ALIGN -} ref_fsm_state_t; - class ControllerCore; class RefreshManager : public IRefreshManager, public sc_module diff --git a/DRAMSys/library/src/controller/core/refresh/RefreshManagerBankwise.cpp b/DRAMSys/library/src/controller/core/refresh/RefreshManagerBankwise.cpp index 3d71980f..76e69453 100644 --- a/DRAMSys/library/src/controller/core/refresh/RefreshManagerBankwise.cpp +++ b/DRAMSys/library/src/controller/core/refresh/RefreshManagerBankwise.cpp @@ -41,13 +41,25 @@ using namespace std; - -RefreshManagerBankwise::RefreshManagerBankwise(sc_module_name /*name*/, - ControllerCore &controller) : controllerCore(controller) +RefreshManagerBankwise::RefreshManagerBankwise(sc_module_name, + ControllerCore &controller) : controllerCore(controller), + timing(controller.config.memSpec.refreshTimings[Bank(0)]) { + if (controllerCore.config.ControllerCoreEnableRefPostpone) { + maxpostpone = controllerCore.config.ControllerCoreMaxPostponedARCmd; + } + if (controllerCore.config.ControllerCoreEnableRefPullIn) { + maxpullin = controllerCore.config.ControllerCoreMaxPulledInARCmd; + } for (Bank bank : controller.getBanks()) { + nextPlannedRefreshs[bank] = SC_ZERO_TIME; + arCmdCounter[bank] = 0; + alignValue[bank] = 0; + currentState[bank] = ST_REFRESH; + previousState[bank] = ST_REFRESH; + nextState[bank] = ST_REFRESH; setUpDummy(refreshPayloads[bank], bank); - planNextRefresh(bank); + planNextRefresh(bank, timing.tREFI); } } @@ -58,34 +70,28 @@ RefreshManagerBankwise::~RefreshManagerBankwise() bool RefreshManagerBankwise::hasCollision(const ScheduledCommand &command) { Bank bank = command.getBank(); - // Get the last AutoRefresh command for this bank and the time of its end - ScheduledCommand lastAutoRefreshCmd = controllerCore.state->getLastCommand( - Command::AutoRefresh, bank); - sc_time endTimeLastAutoRefreshCmd = lastAutoRefreshCmd.getEnd(); - // Get the time of the next planned refresh for this bank - sc_time timeNextPlannedRefresh = nextPlannedRefreshs[command.getBank()]; - // Collision: - // - the start time of the command is before the end time of the last auto refresh command for this bank - // - the end time of the command is after the next planned refresh for this bank - return command.getStart() < endTimeLastAutoRefreshCmd - || command.getEnd() > timeNextPlannedRefresh; + bool collisionWithPreviousRefEnd = command.getStart() < + controllerCore.state->getLastCommand(Command::AutoRefresh, bank).getEnd(); + bool collisionWithNextRefStart = command.getEnd() >= nextPlannedRefreshs[bank]; + if (controllerCore.config.ControllerCoreEnableRefPostpone + && (arCmdCounter[bank] < maxpostpone)) { + collisionWithNextRefStart = false; + } + return collisionWithPreviousRefEnd || collisionWithNextRefStart; } -void RefreshManagerBankwise::scheduleRefresh(tlm::tlm_generic_payload &payload, - sc_time time) +void RefreshManagerBankwise::doRefresh(tlm::tlm_generic_payload &payload, + sc_time time) { sc_assert(!isInvalidated(payload, time)); - tlm::tlm_generic_payload &refreshPayload = refreshPayloads[DramExtension::getExtension(payload).getBank()]; - DramExtension &extension = DramExtension::getExtension(refreshPayload); if (controllerCore.state->rowBufferStates->rowBufferIsOpen( extension.getBank())) { ScheduledCommand precharge(Command::Precharge, time, getExecutionTime(Command::Precharge, refreshPayload), extension); - controllerCore.getCommandChecker(Command::Precharge).delayToSatisfyConstraints( precharge); controllerCore.state->change(precharge); @@ -99,14 +105,135 @@ void RefreshManagerBankwise::scheduleRefresh(tlm::tlm_generic_payload &payload, controllerCore.state->change(refresh); extension.incrementRow(); controllerCore.controller.send(refresh, refreshPayload); - - planNextRefresh(extension.getBank()); } -void RefreshManagerBankwise::planNextRefresh(Bank bank) +void RefreshManagerBankwise::scheduleRefresh(tlm::tlm_generic_payload &payload, + sc_time time) { - nextPlannedRefreshs[bank] += - Configuration::getInstance().memSpec.refreshTimings[bank].tREFI; + sc_time nextRefTiming; + Bank bank = DramExtension::getExtension(payload).getBank(); + bool pendingReq = controllerCore.hasPendingRequests(bank); + bool canPostpone = pendingReq && (arCmdCounter[bank] < maxpostpone); + bool canPullIn = !pendingReq && (arCmdCounter[bank] < maxpullin); + + previousState[bank] = currentState[bank]; + currentState[bank] = nextState[bank]; + + switch (currentState[bank]) { + case ST_REFRESH: + // Regular Refresh. It's possible to migrate from this to the flexible + // refresh states + // The arCmdCounter[bank] should always be equal to zero here + assert(arCmdCounter[bank] == 0); + + if (canPostpone) { + nextState[bank] = ST_POSTPONE; + nextRefTiming = SC_ZERO_TIME; + } else if (canPullIn) { + nextState[bank] = ST_PULLIN; + nextRefTiming = SC_ZERO_TIME; // Attempt to burst pull-in + } else { + doRefresh(payload, time); + nextRefTiming = timing.tREFI; + nextState[bank] = ST_REFRESH; + } + break; + + case ST_PULLIN: + // Pull-In Refresh. Try to pull-in refreshes as long as the limit + // hasn't been reached yet and has credits + + if (canPullIn) { + doRefresh(payload, time); + arCmdCounter[bank]++; + nextState[bank] = ST_PULLIN; + nextRefTiming = timing.tRFC; + } else { + alignValue[bank] = arCmdCounter[bank]; // Saving value to be used by ST_ALIGN + nextState[bank] = ST_ALIGN; + nextRefTiming = SC_ZERO_TIME; + } + break; + + case ST_SKIP: + // Skip Refresh. The arCmdCounter[bank] is used to skip the correct + // amount of refreshes + + arCmdCounter[bank]--; + if (arCmdCounter[bank] == 0) { + nextState[bank] = ST_REFRESH; + nextRefTiming = SC_ZERO_TIME; + } else { + nextState[bank] = ST_SKIP; + nextRefTiming = timing.tREFI; + } + break; + + case ST_POSTPONE: + // Postpone Refresh. Delaying refreshes as long as there are pending + // requests and credits to postpone. Should be followed by a burst + // refresh. + + if ((arCmdCounter[bank] == maxpostpone) || ((!pendingReq) + && !controllerCore.config.ControllerCoreForceMaxRefBurst)) { + // Burst conditions met + if (arCmdCounter[bank] < maxpostpone) { + // In case the burst was started by inactivity, need to also + // count the current REF + arCmdCounter[bank]++; + } + // Will start a burst next, so the value is saved to be used by + // ST_ALIGN + alignValue[bank] = arCmdCounter[bank]; + nextState[bank] = ST_BURST; + nextRefTiming = SC_ZERO_TIME; + } else { + arCmdCounter[bank]++; + nextState[bank] = ST_POSTPONE; + nextRefTiming = timing.tREFI; + } + break; + + case ST_BURST: + // Burst Refresh. The arCmdCounter[bank] is used to issue the correct + // amount of refreshes + + arCmdCounter[bank]--; + doRefresh(payload, time); + if (arCmdCounter[bank] == 0) { + // All bursts issued, next state will align to tREFI + nextState[bank] = ST_ALIGN; + nextRefTiming = SC_ZERO_TIME; + } else { + nextState[bank] = ST_BURST; + nextRefTiming = timing.tRFC; + } + break; + + case ST_ALIGN: + // Align Refresh. Adjusting the timing so the next REF timing will be + // a in a time multiple of tREFI. + + if (previousState[bank] == ST_PULLIN) { + nextRefTiming = timing.tREFI - (timing.tRFC * (alignValue[bank])); + nextState[bank] = ST_SKIP; + } else { + nextRefTiming = timing.tREFI - (timing.tRFC * (alignValue[bank] - 1)); + nextState[bank] = ST_REFRESH; + } + break; + + default: + SC_REPORT_FATAL(this->name(), "Invalid State in Flexible Refresh FSM. Stop."); + break; + } + + planNextRefresh(bank, nextRefTiming); +} + +void RefreshManagerBankwise::planNextRefresh(Bank bank, sc_time nextRefTiming) +{ + nextPlannedRefreshs[bank] += nextRefTiming; controllerCore.controller.send(REFTrigger, nextPlannedRefreshs[bank], refreshPayloads[bank]); } @@ -114,7 +241,7 @@ void RefreshManagerBankwise::planNextRefresh(Bank bank) void RefreshManagerBankwise::reInitialize(Bank bank, sc_time time) { nextPlannedRefreshs[bank] = clkAlign(time, Alignment::DOWN); - planNextRefresh(bank); + planNextRefresh(bank, timing.tREFI); } bool RefreshManagerBankwise::isInvalidated(tlm::tlm_generic_payload &payload, diff --git a/DRAMSys/library/src/controller/core/refresh/RefreshManagerBankwise.h b/DRAMSys/library/src/controller/core/refresh/RefreshManagerBankwise.h index f0f57070..b5c2fc80 100644 --- a/DRAMSys/library/src/controller/core/refresh/RefreshManagerBankwise.h +++ b/DRAMSys/library/src/controller/core/refresh/RefreshManagerBankwise.h @@ -53,19 +53,24 @@ public: virtual bool hasCollision(const ScheduledCommand &command) override; virtual void scheduleRefresh(tlm::tlm_generic_payload &payload, sc_time time) override; - void reInitialize(Bank bank, sc_time time) override; - bool isInvalidated(tlm::tlm_generic_payload &payload, sc_time time) override; private: ControllerCore &controllerCore; - + RefreshTiming &timing; std::map refreshPayloads; std::map nextPlannedRefreshs; + unsigned int maxpostpone = 0; + unsigned int maxpullin = 0; + std::map arCmdCounter; + std::map alignValue; + std::map currentState; + std::map previousState; + std::map nextState; - void planNextRefresh(Bank bank); - + void doRefresh(tlm::tlm_generic_payload &payload, sc_time time); + void planNextRefresh(Bank bank, sc_time nextRefTiming); void printDebugMessage(std::string message); }; diff --git a/DRAMSys/library/src/controller/core/scheduling/checker/ActBChecker.cpp b/DRAMSys/library/src/controller/core/scheduling/checker/ActBChecker.cpp new file mode 100644 index 00000000..841ba5a7 --- /dev/null +++ b/DRAMSys/library/src/controller/core/scheduling/checker/ActBChecker.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2017, University of Kaiserslautern + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Éder F. Zulian + */ + +#include +#include +#include +#include "ActBChecker.h" +#include "../../TimingCalculation.h" +#include "../../../../common/DebugManager.h" +#include "../../../Command.h" +#include "../../../../common/Utils.h" + +using namespace std; + +void ActBChecker::delayToSatisfyConstraints(ScheduledCommand &cmd) const +{ + sc_assert(cmd.getCommand() == Command::ActB); + ScheduledCommand lcb = state.getLastScheduledCommand(cmd.getBank()); + if (lcb.isValidCommand()) { + if (lcb.getCommand() == Command::PreB) { + cmd.establishMinDistanceFromStart(lcb.getStart(), + Configuration::getInstance().getTrpb()); + } else if (lcb.getCommand() == Command::Precharge + || lcb.getCommand() == Command::PrechargeAll) { + cmd.establishMinDistanceFromStart(lcb.getStart(), config.memSpec.tRP); + } else if (lcb.getCommand() == Command::ReadA) { + cmd.establishMinDistanceFromStart(lcb.getStart(), + config.memSpec.tRTP + config.memSpec.tRP); + } else if (lcb.getCommand() == Command::WriteA) { + cmd.establishMinDistanceFromStart(lcb.getStart(), + config.memSpec.tWL + getWriteAccessTime() + config.memSpec.tWR + + config.memSpec.tRP); + } else if (lcb.getCommand() == Command::AutoRefresh) { + cmd.establishMinDistanceFromStart(lcb.getStart(), config.memSpec.tRFC); + } else if (lcb.getCommand() == Command::PDNPX + || lcb.getCommand() == Command::PDNAX) { + cmd.establishMinDistanceFromStart(lcb.getStart(), config.memSpec.tXP); + } else if (lcb.getCommand() == Command::SREFX) { + cmd.establishMinDistanceFromStart(lcb.getStart(), config.memSpec.tXSR); + } else { + reportFatal("ActB Checker", + "ActB can not follow " + commandToString(lcb.getCommand())); + } + } + ScheduledCommand lc; + if ((lc = state.getLastCommand(Command::PrechargeAll)).isValidCommand()) { + cmd.establishMinDistanceFromStart(lc.getStart(), config.memSpec.tRP); + } + delay_to_satisfy_activateToActivate_sameBank(cmd); + while (!(state.bus.isFree(cmd.getStart()) + && satsfies_activateToActivate_differentBank(cmd) + && satisfies_nActivateWindow(cmd))) { + cmd.delayStart(config.memSpec.clk); + } +} + +void ActBChecker::delay_to_satisfy_activateToActivate_sameBank( + ScheduledCommand &cmd) const +{ + ScheduledCommand lastActOnBank = state.getLastCommand(Command::Activate, + cmd.getBank()); + if (lastActOnBank.isValidCommand()) { + cmd.establishMinDistanceFromStart(lastActOnBank.getStart(), config.memSpec.tRC); + } + ScheduledCommand lastActBOnBank = state.getLastCommand(Command::ActB, + cmd.getBank()); + if (lastActBOnBank.isValidCommand()) { + cmd.establishMinDistanceFromStart(lastActBOnBank.getStart(), + Configuration::getInstance().getTrcb()); + } +} + +bool ActBChecker::satsfies_activateToActivate_differentBank( + ScheduledCommand &cmd) const +{ + for (auto act : state.lastActivatesB) { + sc_time t = act.first, tRRD = (cmd.getBankGroup() == act.second.getBankGroup() ? + Configuration::getInstance().getTrrdb_L() : + Configuration::getInstance().getTrrdb_S()); + if ((t < cmd.getStart() && cmd.getStart() - t < tRRD) || (cmd.getStart() <= t + && t - cmd.getStart() < tRRD)) { + return false; + } + } + for (auto act : state.lastActivates) { + sc_time t = act.first, tRRD = (cmd.getBankGroup() == act.second.getBankGroup() ? + config.memSpec.tRRD_L : config.memSpec.tRRD_S); + if ((t < cmd.getStart() && cmd.getStart() - t < tRRD) || (cmd.getStart() <= t + && t - cmd.getStart() < tRRD)) { + return false; + } + } + return true; +} + +bool ActBChecker::satisfies_nActivateWindow(ScheduledCommand &cmd) const +{ + if (state.lastActivatesB.size() >= config.memSpec.nActivate) { + maplastActivates = state.lastActivatesB; + lastActivates.emplace(cmd.getStart(), cmd); + auto upper = lastActivates.begin(); + advance(upper, config.memSpec.nActivate); + auto lower = lastActivates.begin(); + while (upper != lastActivates.end()) { + if (upper->first - lower->first < Configuration::getInstance().getTfawb()) { + return false; + } + ++upper; + ++lower; + } + } + return true; +} diff --git a/DRAMSys/library/src/controller/core/scheduling/checker/ActBChecker.h b/DRAMSys/library/src/controller/core/scheduling/checker/ActBChecker.h new file mode 100644 index 00000000..023db94f --- /dev/null +++ b/DRAMSys/library/src/controller/core/scheduling/checker/ActBChecker.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017, University of Kaiserslautern + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Éder F. Zulian + */ +#ifndef ACTB_CHECKER_H_ +#define ACTB_CHECKER_H_ +#include +#include "ICommandChecker.h" +#include "../../configuration/Configuration.h" +#include "../../../ControllerState.h" +class ActBChecker: public ICommandChecker +{ +public: + ActBChecker(const Configuration &config, + ControllerState &state) : config(config), state(state) {} + virtual ~ActBChecker() {} + virtual void delayToSatisfyConstraints(ScheduledCommand &command) const + override; +private: + const Configuration &config; + ControllerState &state; + void delay_to_satisfy_activateToActivate_sameBank(ScheduledCommand &command) + const; + bool satsfies_activateToActivate_differentBank(ScheduledCommand &command) const; + bool satisfies_nActivateWindow(ScheduledCommand &command) const; +}; +#endif /* ACTB_CHECKER_H_ */ diff --git a/DRAMSys/library/src/controller/core/scheduling/checker/ActivateChecker.cpp b/DRAMSys/library/src/controller/core/scheduling/checker/ActivateChecker.cpp index 4ce4b249..da2b67cb 100644 --- a/DRAMSys/library/src/controller/core/scheduling/checker/ActivateChecker.cpp +++ b/DRAMSys/library/src/controller/core/scheduling/checker/ActivateChecker.cpp @@ -52,13 +52,20 @@ void ActivateChecker::delayToSatisfyConstraints(ScheduledCommand &command) const ScheduledCommand lastCommandOnBank = state.getLastScheduledCommand( command.getBank()); if (lastCommandOnBank.isValidCommand()) { - if (lastCommandOnBank.getCommand() == Command::Precharge + if (lastCommandOnBank.getCommand() == Command::PreB + || lastCommandOnBank.getCommand() == Command::Precharge || lastCommandOnBank.getCommand() == Command::PrechargeAll) { command.establishMinDistanceFromStart(lastCommandOnBank.getStart(), config.memSpec.tRP); } else if (lastCommandOnBank.getCommand() == Command::ReadA) { command.establishMinDistanceFromStart(lastCommandOnBank.getStart(), config.memSpec.tRTP + config.memSpec.tRP); + } else if (lastCommandOnBank.getCommand() == Command::WriteA) { + command.establishMinDistanceFromStart(lastCommandOnBank.getStart(), + config.memSpec.tRP); + } else if (lastCommandOnBank.getCommand() == Command::ReadA) { + command.establishMinDistanceFromStart(lastCommandOnBank.getStart(), + config.memSpec.tRTP + config.memSpec.tRP); } else if (lastCommandOnBank.getCommand() == Command::WriteA) { command.establishMinDistanceFromStart(lastCommandOnBank.getStart(), config.memSpec.tWL + getWriteAccessTime() + config.memSpec.tWR + @@ -97,6 +104,13 @@ void ActivateChecker::delay_to_satisfy_activateToActivate_sameBank( command.establishMinDistanceFromStart(lastActivateOnBank.getStart(), config.memSpec.tRC); } + + ScheduledCommand lastActBOnBank = state.getLastCommand(Command::ActB, + command.getBank()); + if (lastActBOnBank.isValidCommand()) { + command.establishMinDistanceFromStart(lastActivateOnBank.getStart(), + config.memSpec.tRC); + } } bool ActivateChecker::satsfies_activateToActivate_differentBank( diff --git a/DRAMSys/library/src/controller/core/scheduling/checker/PreBChecker.cpp b/DRAMSys/library/src/controller/core/scheduling/checker/PreBChecker.cpp new file mode 100644 index 00000000..a8376a28 --- /dev/null +++ b/DRAMSys/library/src/controller/core/scheduling/checker/PreBChecker.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2017, University of Kaiserslautern + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Éder F. Zulian + */ + +#include "PreBChecker.h" +#include "../../TimingCalculation.h" +void PreBChecker::delayToSatisfyConstraints(ScheduledCommand &cmd)const +{ + sc_assert(cmd.getCommand() == Command::PreB); + ScheduledCommand lastCmd = state.getLastScheduledCommand(cmd.getBank()); + if (lastCmd.isValidCommand()) { + if (lastCmd.getCommand() == Command::PreB) { + cmd.establishMinDistanceFromStart(lastCmd.getStart(), + Configuration::getInstance().getTrpb()); + } else if (lastCmd.getCommand() == Command::Precharge) { + cmd.establishMinDistanceFromStart(lastCmd.getStart(), config.memSpec.tRP); + } else if (lastCmd.getCommand() == Command::PrechargeAll) { + cmd.establishMinDistanceFromStart(lastCmd.getStart(), config.memSpec.tRP); + } else if (lastCmd.getCommand() == Command::ActB) { + cmd.establishMinDistanceFromStart(lastCmd.getStart(), + config.memSpec.tRCD); // XXX: trcd is less than the NEW! trasb! ok! + } else if (lastCmd.getCommand() == Command::Activate) { + cmd.establishMinDistanceFromStart(lastCmd.getStart(), config.memSpec.tRCD); + } else if (lastCmd.getCommand() == Command::Read) { + cmd.establishMinDistanceFromStart(lastCmd.getStart(), config.memSpec.tRTP); + } else if (lastCmd.getCommand() == Command::Write) { + cmd.establishMinDistanceFromStart(lastCmd.getStart(), + config.memSpec.tWL + getWriteAccessTime() + config.memSpec.tWR); + } else if (lastCmd.getCommand() == Command::PDNAX) { + cmd.establishMinDistanceFromStart(lastCmd.getStart(), config.memSpec.tXP); + } else { + reportFatal("PreB Checker", + "PreB can not follow " + commandToString(lastCmd.getCommand())); + } + } + ScheduledCommand lc; + if ((lc = state.getLastCommand(Command::PrechargeAll)).isValidCommand()) { + cmd.establishMinDistanceFromStart(lc.getStart(), config.memSpec.tRP); + } + if ((lc = state.getLastCommand(Command::Activate, + cmd.getBank())).isValidCommand()) { + cmd.establishMinDistanceFromStart(lc.getStart(), config.memSpec.tRAS); + } + if ((lc = state.getLastCommand(Command::ActB, + cmd.getBank())).isValidCommand()) { + cmd.establishMinDistanceFromStart(lc.getStart(), + Configuration::getInstance().getTrasb()); + } + state.bus.moveCommandToNextFreeSlot(cmd); +} diff --git a/DRAMSys/library/src/controller/core/scheduling/checker/PreBChecker.h b/DRAMSys/library/src/controller/core/scheduling/checker/PreBChecker.h new file mode 100644 index 00000000..f37241d9 --- /dev/null +++ b/DRAMSys/library/src/controller/core/scheduling/checker/PreBChecker.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017, University of Kaiserslautern + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Éder F. Zulian + */ +#ifndef PREB_CHECKER_H_ +#define PREB_CHECKER_H_ +#include "ICommandChecker.h" +#include "../../configuration/Configuration.h" +#include "../../../ControllerState.h" +class PreBChecker: public ICommandChecker +{ +public: + PreBChecker(const Configuration &config, + ControllerState &state): config(config), state(state) {} + virtual ~PreBChecker() {} + virtual void delayToSatisfyConstraints(ScheduledCommand &command) const + override; +private: + const Configuration &config; + ControllerState &state; +}; +#endif /* PREB_CHECKER_H_ */ diff --git a/DRAMSys/library/src/controller/core/scheduling/checker/PrechargeAllChecker.cpp b/DRAMSys/library/src/controller/core/scheduling/checker/PrechargeAllChecker.cpp index 297ad105..12f743c9 100644 --- a/DRAMSys/library/src/controller/core/scheduling/checker/PrechargeAllChecker.cpp +++ b/DRAMSys/library/src/controller/core/scheduling/checker/PrechargeAllChecker.cpp @@ -48,10 +48,12 @@ const for (unsigned int bank = 0; bank < config.memSpec.NumberOfBanks; ++bank) { ScheduledCommand lastCommand = state.getLastScheduledCommand(Bank(bank)); if (lastCommand.isValidCommand()) { - if (lastCommand.getCommand() == Command::Precharge) { + if (lastCommand.getCommand() == Command::Precharge + || lastCommand.getCommand() == Command::PreB) { command.establishMinDistanceFromStart(lastCommand.getStart(), config.memSpec.tRP); - } else if (lastCommand.getCommand() == Command::Activate) { + } else if (lastCommand.getCommand() == Command::Activate + || lastCommand.getCommand() == Command::ActB) { command.establishMinDistanceFromStart(lastCommand.getStart(), config.memSpec.tRCD); } else if (lastCommand.getCommand() == Command::Read) { diff --git a/DRAMSys/library/src/controller/core/scheduling/checker/PrechargeChecker.cpp b/DRAMSys/library/src/controller/core/scheduling/checker/PrechargeChecker.cpp index d6bfd8fd..c51fb2b1 100644 --- a/DRAMSys/library/src/controller/core/scheduling/checker/PrechargeChecker.cpp +++ b/DRAMSys/library/src/controller/core/scheduling/checker/PrechargeChecker.cpp @@ -49,10 +49,12 @@ const if (lastCommand.isValidCommand()) { // the first two cases happen when a resfresh interrups the command sequence of a transaction // (e.g. commands to process transaction are PRE-ACT-RD and refresh happens after the PRE or after the ACT) - if (lastCommand.getCommand() == Command::Precharge) { + if (lastCommand.getCommand() == Command::Precharge + || lastCommand.getCommand() == Command::PreB) { command.establishMinDistanceFromStart(lastCommand.getStart(), config.memSpec.tRP); - } else if (lastCommand.getCommand() == Command::Activate) { + } else if (lastCommand.getCommand() == Command::Activate + || lastCommand.getCommand() == Command::ActB) { command.establishMinDistanceFromStart(lastCommand.getStart(), config.memSpec.tRCD); } diff --git a/DRAMSys/library/src/controller/core/scheduling/checker/ReadChecker.cpp b/DRAMSys/library/src/controller/core/scheduling/checker/ReadChecker.cpp index 78c3cfe9..b5917e42 100644 --- a/DRAMSys/library/src/controller/core/scheduling/checker/ReadChecker.cpp +++ b/DRAMSys/library/src/controller/core/scheduling/checker/ReadChecker.cpp @@ -49,7 +49,8 @@ void ReadChecker::delayToSatisfyConstraints(ScheduledCommand &command) const ScheduledCommand lastCommand = state.getLastScheduledCommand(command.getBank()); if (lastCommand.isValidCommand()) { - if (lastCommand.getCommand() == Command::Activate) { + if (lastCommand.getCommand() == Command::Activate + || lastCommand.getCommand() == Command::ActB) { command.establishMinDistanceFromStart(lastCommand.getStart(), config.memSpec.tRCD); } else if (lastCommand.getCommand() == Command::Read) { diff --git a/DRAMSys/library/src/controller/core/scheduling/checker/RefreshChecker.cpp b/DRAMSys/library/src/controller/core/scheduling/checker/RefreshChecker.cpp index f05f308d..f7d110f0 100644 --- a/DRAMSys/library/src/controller/core/scheduling/checker/RefreshChecker.cpp +++ b/DRAMSys/library/src/controller/core/scheduling/checker/RefreshChecker.cpp @@ -66,8 +66,6 @@ void RefreshChecker::delayToSatisfyConstraints(ScheduledCommand &command) const command.establishMinDistanceFromStart(lastCommandOnBank.getStart(), config.memSpec.tXSR); } else if (lastCommandOnBank.getCommand() == Command::AutoRefresh) { - command.establishMinDistanceFromStart(lastCommandOnBank.getStart(), - config.memSpec.tRFC); } else reportFatal("Refresh Checker", "Refresh can not follow " + commandToString(lastCommandOnBank.getCommand())); @@ -77,10 +75,12 @@ void RefreshChecker::delayToSatisfyConstraints(ScheduledCommand &command) const ScheduledCommand lastCommand = state.getLastScheduledCommand(Bank(bank)); if (lastCommand.isValidCommand()) { if (lastCommand.getCommand() == Command::Precharge - || lastCommand.getCommand() == Command::PrechargeAll) { + || lastCommand.getCommand() == Command::PrechargeAll + || lastCommand.getCommand() == Command::PreB) { command.establishMinDistanceFromStart(lastCommand.getStart(), config.memSpec.tRP); - } else if (lastCommand.getCommand() == Command::Activate) { + } else if (lastCommand.getCommand() == Command::Activate + || lastCommand.getCommand() == Command::ActB) { command.establishMinDistanceFromStart(lastCommand.getStart(), config.memSpec.tRCD); } else if (lastCommand.getCommand() == Command::ReadA) { @@ -98,8 +98,6 @@ void RefreshChecker::delayToSatisfyConstraints(ScheduledCommand &command) const command.establishMinDistanceFromStart(lastCommand.getStart(), config.memSpec.tXSR); } else if (lastCommand.getCommand() == Command::AutoRefresh) { - command.establishMinDistanceFromStart(lastCommand.getStart(), - config.memSpec.tRFC); } else reportFatal("Refresh Checker", "Refresh can not follow " + commandToString(lastCommand.getCommand())); diff --git a/DRAMSys/library/src/controller/core/scheduling/checker/WriteChecker.cpp b/DRAMSys/library/src/controller/core/scheduling/checker/WriteChecker.cpp index a356caa8..226d24aa 100644 --- a/DRAMSys/library/src/controller/core/scheduling/checker/WriteChecker.cpp +++ b/DRAMSys/library/src/controller/core/scheduling/checker/WriteChecker.cpp @@ -49,7 +49,8 @@ void WriteChecker::delayToSatisfyConstraints(ScheduledCommand &command) const ScheduledCommand lastCommand = state.getLastScheduledCommand(command.getBank()); if (lastCommand.isValidCommand()) { - if (lastCommand.getCommand() == Command::Activate) { + if (lastCommand.getCommand() == Command::Activate + || lastCommand.getCommand() == Command::ActB) { command.establishMinDistanceFromStart(lastCommand.getStart(), config.memSpec.tRCD); } else if (lastCommand.getCommand() == Command::Read) { diff --git a/DRAMSys/library/src/simulation/DRAMSys.cpp b/DRAMSys/library/src/simulation/DRAMSys.cpp index 3d9829db..031e4bc4 100644 --- a/DRAMSys/library/src/simulation/DRAMSys.cpp +++ b/DRAMSys/library/src/simulation/DRAMSys.cpp @@ -128,7 +128,8 @@ DRAMSys::DRAMSys(sc_module_name __attribute__((unused)) name, simName = Configuration::getInstance().SimulationName; tinyxml2::XMLDocument simulationdoc; loadXML(simulationToRun, simulationdoc); - tinyxml2::XMLElement *simulation = simulationdoc.FirstChildElement("simulation"); + tinyxml2::XMLElement *simulation = + simulationdoc.FirstChildElement("simulation"); if (simulation != NULL) { tinyxml2::XMLElement *simid = simulation->FirstChildElement("simulationid"); if (simid != NULL) { @@ -140,8 +141,6 @@ DRAMSys::DRAMSys(sc_module_name __attribute__((unused)) name, } } } - cout << "Simulation name set to: " << simName << endl; - // Instantiate all internal DRAMSys modules: instantiateModules(simName, pathToResources); // Connect all internal DRAMSys modules: @@ -149,6 +148,8 @@ DRAMSys::DRAMSys(sc_module_name __attribute__((unused)) name, // Setup the debug manager: setupDebugManager(Configuration::getInstance().SimulationName); + + report(headline); } void DRAMSys::logo() diff --git a/DRAMSys/library/src/simulation/Dram.h b/DRAMSys/library/src/simulation/Dram.h index c1e0a9cf..e81023a3 100644 --- a/DRAMSys/library/src/simulation/Dram.h +++ b/DRAMSys/library/src/simulation/Dram.h @@ -48,6 +48,7 @@ #include #include #include +#include #include "../common/DebugManager.h" #include "../common/dramExtension.h" #include "../controller/Controller.h" @@ -129,6 +130,13 @@ struct Dram : sc_module { memArchSpec.dll = Configuration::getInstance().memSpec.DLL; MemTimingSpec memTimingSpec; + memTimingSpec.FAWB = Configuration::getInstance().tfawbclk; + memTimingSpec.RASB = Configuration::getInstance().trasbclk; + memTimingSpec.RCB = Configuration::getInstance().trcbclk; + memTimingSpec.RPB = Configuration::getInstance().trpbclk; + memTimingSpec.RRDB = Configuration::getInstance().trrdblclk; + memTimingSpec.RRDB_L = Configuration::getInstance().trrdblclk; + memTimingSpec.RRDB_S = Configuration::getInstance().trrdblclk; memTimingSpec.AL = Configuration::getInstance().memSpec.tAL / clk; memTimingSpec.CCD = Configuration::getInstance().memSpec.tCCD_S / clk; memTimingSpec.CCD_L = Configuration::getInstance().memSpec.tCCD_L / clk; @@ -388,7 +396,13 @@ struct Dram : sc_module { cycle = sc_time_stamp().value() / Configuration::getInstance().memSpec.clk.value(); } - if (phase == BEGIN_PRE) { + if (phase == BEGIN_PREB) { + if (powerAnalysis == true) { + DRAMPower->doCommand(MemCommand::PREB, bank, cycle); + } + sendToController(payload, END_PREB, delay + getExecutionTime(Command::PreB, + payload)); + } else if (phase == BEGIN_PRE) { if (powerAnalysis == true) { DRAMPower->doCommand(MemCommand::PRE, bank, cycle); } @@ -400,6 +414,16 @@ struct Dram : sc_module { } sendToController(payload, END_PRE_ALL, delay + getExecutionTime(Command::PrechargeAll, payload)); + } else if (phase == BEGIN_ACTB) { + if (powerAnalysis == true) { + DRAMPower->doCommand(MemCommand::ACTB, bank, cycle); + } + sendToController(payload, END_ACTB, delay + getExecutionTime(Command::ActB, + payload)); + unsigned int row = DramExtension::getExtension(payload).getRow().ID(); + if (StoreMode == StorageMode::ErrorModel) { + ememory[bank]->activate(row); + } } else if (phase == BEGIN_ACT) { if (powerAnalysis == true) { DRAMPower->doCommand(MemCommand::ACT, bank, cycle); @@ -502,7 +526,7 @@ struct Dram : sc_module { else if (phase == BEGIN_REFB) { if (powerAnalysis == true) { - SC_REPORT_FATAL("DRAM", "Power calculation for bankwise logic not supported"); + DRAMPower->doCommand(MemCommand::REFB, bank, cycle); } sendToController(payload, END_REFB, delay + getExecutionTime(Command::AutoRefresh, payload)); diff --git a/DRAMSys/library/src/simulation/TracePlayer.cpp b/DRAMSys/library/src/simulation/TracePlayer.cpp index 581098a3..e46840b3 100644 --- a/DRAMSys/library/src/simulation/TracePlayer.cpp +++ b/DRAMSys/library/src/simulation/TracePlayer.cpp @@ -115,6 +115,7 @@ void TracePlayer::setNumberOfTransactions(unsigned int n) unsigned int TracePlayer::getNumberOfLines(string pathToTrace) { + // Reference: http://stackoverflow.com/questions/3482064/counting-the-number-of-lines-in-a-text-file ifstream newFile; newFile.open(pathToTrace); // new lines will be skipped unless we stop it from happening: diff --git a/DRAMSys/simulator/main.cpp b/DRAMSys/simulator/main.cpp index 39fb82d7..798e685a 100644 --- a/DRAMSys/simulator/main.cpp +++ b/DRAMSys/simulator/main.cpp @@ -69,7 +69,7 @@ int sc_main(int argc, char **argv) if (argc > 1) { SimulationXML = argv[1]; } else { - SimulationXML = resources + "simulations/ddr3-example.xml"; + SimulationXML = resources + "simulations/rgrsim.xml"; } std::vector players; diff --git a/DRAMSys/traceAnalyzer/businessObjects/phases/phase.h b/DRAMSys/traceAnalyzer/businessObjects/phases/phase.h index 952e1f2c..5de756e0 100644 --- a/DRAMSys/traceAnalyzer/businessObjects/phases/phase.h +++ b/DRAMSys/traceAnalyzer/businessObjects/phases/phase.h @@ -142,6 +142,21 @@ protected: }; +class PREB: public Phase +{ +public: + using Phase::Phase; +protected: + virtual QColor getPhaseColor() const override + { + return ColorGenerator::getColor(1); + } + virtual QString Name() const override + { + return "PREB"; + } +}; + class PRE : public Phase { public: @@ -157,6 +172,20 @@ protected: } }; +class ACTB : public Phase +{ +public: + using Phase::Phase; +protected: + virtual QColor getPhaseColor() const override + { + return ColorGenerator::getColor(3); + } + virtual QString Name() const override + { + return "ACTB"; + } +}; class ACT : public Phase { diff --git a/DRAMSys/traceAnalyzer/businessObjects/phases/phasefactory.cpp b/DRAMSys/traceAnalyzer/businessObjects/phases/phasefactory.cpp index 1d3393a7..3f7dc143 100644 --- a/DRAMSys/traceAnalyzer/businessObjects/phases/phasefactory.cpp +++ b/DRAMSys/traceAnalyzer/businessObjects/phases/phasefactory.cpp @@ -56,9 +56,15 @@ shared_ptr PhaseFactory::CreatePhase(ID id, const QString &dbPhaseName, return shared_ptr(new RESP(id, span, trans, {}, std::shared_ptr())); + else if (dbPhaseName == "PREB") return shared_ptr(new PREB(id, span, + trans, {Timespan(span.Begin(), span.Begin() + clk)}, + std::shared_ptr())); else if (dbPhaseName == "PRE") return shared_ptr(new PRE(id, span, trans, {Timespan(span.Begin(), span.Begin() + clk)}, std::shared_ptr())); + else if (dbPhaseName == "ACTB") return shared_ptr(new ACTB(id, span, + trans, {Timespan(span.Begin(), span.Begin() + clk)}, + std::shared_ptr())); else if (dbPhaseName == "ACT") return shared_ptr(new ACT(id, span, trans, {Timespan(span.Begin(), span.Begin() + clk)}, std::shared_ptr())); diff --git a/DRAMSys/traceAnalyzer/data/tracedb.cpp b/DRAMSys/traceAnalyzer/data/tracedb.cpp index b1b19a65..dbe3687a 100644 --- a/DRAMSys/traceAnalyzer/data/tracedb.cpp +++ b/DRAMSys/traceAnalyzer/data/tracedb.cpp @@ -172,6 +172,30 @@ shared_ptr TraceDB::getNextPrecharge(ID currentTransactionId) return parseTransactionFromQuery(query); } +shared_ptr TraceDB::getNextActb(ID currentTransactionId) +{ + QSqlQuery query(database); + QString queryText = queryTexts.queryHead + + "WHERE TransactionID > :currentID AND PhaseName = 'ACTB' LIMIT 1"; + + query.prepare(queryText); + query.bindValue(":currentID", currentTransactionId); + executeQuery(query); + return parseTransactionFromQuery(query); +} + +shared_ptr TraceDB::getNextPreb(ID currentTransactionId) +{ + QSqlQuery query(database); + QString queryText = queryTexts.queryHead + + "WHERE TransactionID > :currentID AND PhaseName = 'PREB' LIMIT 1"; + + query.prepare(queryText); + query.bindValue(":currentID", currentTransactionId); + executeQuery(query); + return parseTransactionFromQuery(query); +} + shared_ptr TraceDB::getNextRefresh(ID currentTransactionId) { QSqlQuery query(database); @@ -184,6 +208,18 @@ shared_ptr TraceDB::getNextRefresh(ID currentTransactionId) return parseTransactionFromQuery(query); } +shared_ptr TraceDB::getNextRefb(ID currentTransactionId) +{ + QSqlQuery query(database); + QString queryText = queryTexts.queryHead + + "WHERE TransactionID > :currentID AND PhaseName = 'REFB' LIMIT 1"; + + query.prepare(queryText); + query.bindValue(":currentID", currentTransactionId); + executeQuery(query); + return parseTransactionFromQuery(query); +} + ID TraceDB::getTransactionIDFromPhaseID(ID phaseID) { QSqlQuery query(database); diff --git a/DRAMSys/traceAnalyzer/data/tracedb.h b/DRAMSys/traceAnalyzer/data/tracedb.h index b4baf8bb..7ab07040 100644 --- a/DRAMSys/traceAnalyzer/data/tracedb.h +++ b/DRAMSys/traceAnalyzer/data/tracedb.h @@ -83,6 +83,9 @@ public: std::shared_ptr getNextPrecharge(ID currentTransactionId); std::shared_ptr getNextActivate(ID currentTransactionId); std::shared_ptr getNextRefresh(ID currentTransactionId); + std::shared_ptr getNextPreb(ID currentTransactionId); + std::shared_ptr getNextActb(ID currentTransactionId); + std::shared_ptr getNextRefb(ID currentTransactionId); std::shared_ptr getTransactionByID(ID id); diff --git a/DRAMSys/traceAnalyzer/presentation/tracenavigator.cpp b/DRAMSys/traceAnalyzer/presentation/tracenavigator.cpp index 4d2a15b2..01f7db86 100644 --- a/DRAMSys/traceAnalyzer/presentation/tracenavigator.cpp +++ b/DRAMSys/traceAnalyzer/presentation/tracenavigator.cpp @@ -234,7 +234,45 @@ void TraceNavigator::selectNextPrecharge() selectTransaction(nextPrecharge); } +void TraceNavigator::selectNextActb() +{ + shared_ptr nextActb; + if (!SelectedTransactions().empty()) + nextActb = traceFile.getNextActb(SelectedTransactions().front()->Id()); + else + nextActb = traceFile.getNextActb(0); + + if (nextActb) + selectTransaction(nextActb); +} + +void TraceNavigator::selectNextPreb() +{ + shared_ptr nextPreb; + + if (!SelectedTransactions().empty()) + nextPreb = traceFile.getNextPreb( + SelectedTransactions().front()->Id()); + else + nextPreb = traceFile.getNextPreb(0); + + if (nextPreb) + selectTransaction(nextPreb); +} + +void TraceNavigator::selectNextRefb() +{ + shared_ptr n; + + if (!SelectedTransactions().empty()) + n = traceFile.getNextRefb(SelectedTransactions().front()->Id()); + else + n = traceFile.getNextRefb(0); + + if (n) + selectTransaction(n); +} bool TraceNavigator::transactionIsSelected(const shared_ptr &transaction) const diff --git a/DRAMSys/traceAnalyzer/presentation/tracenavigator.h b/DRAMSys/traceAnalyzer/presentation/tracenavigator.h index a31591d7..357da700 100644 --- a/DRAMSys/traceAnalyzer/presentation/tracenavigator.h +++ b/DRAMSys/traceAnalyzer/presentation/tracenavigator.h @@ -87,6 +87,9 @@ public: void selectNextRefresh(); void selectNextActivate(); void selectNextPrecharge(); + void selectNextActb(); + void selectNextPreb(); + void selectNextRefb(); void addSelectedTransactions(const std::vector> &transactions); diff --git a/DRAMSys/traceAnalyzer/presentation/traceplot.cpp b/DRAMSys/traceAnalyzer/presentation/traceplot.cpp index f2020e7c..65434781 100644 --- a/DRAMSys/traceAnalyzer/presentation/traceplot.cpp +++ b/DRAMSys/traceAnalyzer/presentation/traceplot.cpp @@ -111,6 +111,24 @@ void TracePlot::setUpActions() QObject::connect(selectNextPrecharge, SIGNAL(triggered()), this, SLOT(on_selectNextPrecharge())); + selectNextActb = new QAction("Select next atcb", this); + selectNextActb->setShortcut(QKeySequence("alt+b")); + addAction(selectNextActb); + QObject::connect(selectNextActb, SIGNAL(triggered()), this, + SLOT(on_selectNextActb())); + + selectNextPreb = new QAction("Select next preb", this); + selectNextPreb->setShortcut(QKeySequence("alt+q")); + addAction(selectNextPreb); + QObject::connect(selectNextPreb, SIGNAL(triggered()), this, + SLOT(on_selectNextPreb())); + + selectNextRefb = new QAction("Select next refb", this); + selectNextRefb->setShortcut(QKeySequence("alt+s")); + addAction(selectNextRefb); + QObject::connect(selectNextRefb, SIGNAL(triggered()), this, + SLOT(on_selectNextRefb())); + setColorGroupingPhase = new QAction("Group by Phase", this); addAction(setColorGroupingPhase); QObject::connect(setColorGroupingPhase, SIGNAL(triggered()), this, @@ -148,7 +166,7 @@ void TracePlot::setUpContextMenu() contextMenu->addMenu(goToSubMenu); QMenu *selectSubMenu = new QMenu("Select", contextMenu); - selectSubMenu->addActions({selectNextRefresh, selectNextActivate, selectNextPrecharge}); + selectSubMenu->addActions({selectNextRefresh, selectNextActivate, selectNextPrecharge, selectNextActb, selectNextPreb, selectNextRefb}); contextMenu->addMenu(selectSubMenu); contextMenu->addActions({showQueryEditor, insertComment, exportToPdf}); @@ -365,6 +383,21 @@ void TracePlot::on_selectNextPrecharge() navigator->selectNextPrecharge(); } +void TracePlot::on_selectNextActb() +{ + navigator->selectNextActb(); +} + +void TracePlot::on_selectNextPreb() +{ + navigator->selectNextPreb(); +} + +void TracePlot::on_selectNextRefb() +{ + navigator->selectNextRefb(); +} + void TracePlot::on_colorGroupingPhase() { drawingProperties.colorGrouping = ColorGrouping::PhaseType; diff --git a/DRAMSys/traceAnalyzer/presentation/traceplot.h b/DRAMSys/traceAnalyzer/presentation/traceplot.h index 322102b2..5238b7ec 100644 --- a/DRAMSys/traceAnalyzer/presentation/traceplot.h +++ b/DRAMSys/traceAnalyzer/presentation/traceplot.h @@ -91,6 +91,9 @@ private Q_SLOTS: void on_selectNextRefresh(); void on_selectNextActivate(); void on_selectNextPrecharge(); + void on_selectNextActb(); + void on_selectNextPreb(); + void on_selectNextRefb(); void on_colorGroupingPhase(); void on_colorGroupingTransaction(); void on_colorGroupingThread(); @@ -154,6 +157,9 @@ private: QAction *selectNextRefresh; QAction *selectNextActivate; QAction *selectNextPrecharge; + QAction *selectNextActb; + QAction *selectNextPreb; + QAction *selectNextRefb; QAction *setColorGroupingPhase; QAction *setColorGroupingTransaction; QAction *setColorGroupingThread; diff --git a/README.md b/README.md index 75e7c5da..73078ec6 100644 --- a/README.md +++ b/README.md @@ -455,6 +455,12 @@ Below, the sub-configurations are listed and explained. - Size of the window in clock cycles used to evaluate average bandwidth and average power consumption - *NumberOfMemChannels* (unsigned int) - Number of memory channels + - *ControllerCoreDisableRefresh* (boolean) + - "1": disables refreshes + - "0": normal operation (refreshes enabled) + - *ControllerCoreRowGranularRef* (boolean) + - "1": enable row granular refresh + - "0": normal operation - *ThermalSimulation* (boolean) - "1": enables thermal simulation - "0": static temperature during simulation @@ -668,12 +674,10 @@ Below, the sub-configurations are listed and explained. - **Memory Controller Configuration** - The content of [fifo.xml](DRAMSys/library/resources/configs/mcconfigs/fifo.xml) is - presented below as an example. + An example follows. ``` xml - @@ -681,13 +685,44 @@ Below, the sub-configurations are listed and explained. - + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ``` @@ -740,8 +775,103 @@ Below, the sub-configurations are listed and explained. - Max AR commands to be postponed. - *ControllerCoreMaxPulledInARCmd* (unsigned int) - Max AR commands to be pulled-in. + - *ControllerCoreRowGranularRef* (boolean) + - "1": enables row granular refresh feature (RGR) + - "0": normal operation + - *ControllerCoreRowGranularRefNumAR* (unsigned int) + - Number of AR commands to to be issued in a refresh period tREFI + - *ControllerCoreRowGranularRefRowInc* (unsigned int) + - Row increment for each AR command (selective refresh) + - *ControllerCoreRowGranularRefB0* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefB1* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefB2* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefB3* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefB4* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefB5* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefB6* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefB7* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefB8* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefB9* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefB10* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefB11* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefB12* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefB13* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefB14* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefB15* (boolean) + - "1": RGR this bank + - "0": skip this bank + - *ControllerCoreRowGranularRefRASBInClkCycles* (unsigned int) + - Timing can be changed to explore optimum row granular refresh (ORGR) + - *ControllerCoreRowGranularRefRRDB_LInClkCycles* (unsigned int) + - Timing can be changed to explore optimum row granular refresh (ORGR) + - *ControllerCoreRowGranularRefRRDB_SInClkCycles* (unsigned int) + - Timing can be changed to explore optimum row granular refresh (ORGR) + - *ControllerCoreRowGranularRefRPBInClkCycles* (unsigned int) + - Timing can be changed to explore optimum row granular refresh (ORGR) + - *ControllerCoreRowGranularRefRCBInClkCycles* (unsigned int) + - Timing can be changed to explore optimum row granular refresh (ORGR) + - *ControllerCoreRowGranularRefFAWBInClkCycles* (unsigned int) + - Timing can be changed to explore optimum row granular refresh (ORGR) + +- **Flexible Refresh** + +The feature can be used together with regular refresh and also with row +granular refresh (RGR). + +**Pull-In Refresh** + +A Pull-In is started when there are no pending requests in the buffer, meaning +the memory is in an idle state. Therefore, in order to prepare for possible +accesses that might happen in the future, a burst of REF commands is +initiated. If, at any point, requests start coming in, the burst is +interrupted, meaning that the maximum amount of time, considering the worst +case scenario (a request arrives at the same time a REF was issued), is tRFC. +The advantage of pulling-in refreshes is that they will not issued in the +near future (in their actual times), allowing for more efficient accesses to +the memory. + +**Postpone Refresh** + +Similarly, the decision to postpone a refresh is done if there are pending +requests on the buffer. Given that having requests might mean a hit, we want +to postpone the refresh to minimize the PREA commands needed. If the memory +enters an idle state, a burst is issued for the same number of REF commands +that were postponed. + +**The Flexible Refresh FSM** + +![Flexible](DRAMSys/docs/images/flexreffsm.png) -For further details on the Flexible Refresh feature, please refer to [this](DRAMSys/docs/flexible-refresh.pdf) document. - **Trace Setups** - *clkMhz* (unsigned int)