Skip to content

Make runtime call hardware init functions automatically #195

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 13, 2021

Conversation

Hazematman
Copy link
Contributor

Modify the c runtime to automatically call initialize functions
for interrupts and for cop1.

Addtionally add cop1 init routine flash denormalized floats to 0.

As a note this pull request also modifies the testrom to mark test_dma_read_misalign as TEST_FLAGS_NO_BENCHMARK. This was done as when running on hardware I found that this test would randomly fall because of the execution time.

Modify the c runtime to automatically call initalize functions
for interrupts and for cop1.

Addtionally add cop1 init routine flash denormalized floats to 0.
Copy link
Collaborator

@rasky rasky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great PR! We're doing a great step here. We finally have constructors working and we simplify the libdragon initialization for beginners.

n64.ld Outdated
@@ -20,6 +20,8 @@
OUTPUT_FORMAT ("elf32-bigmips", "elf32-bigmips", "elf32-littlemips")
OUTPUT_ARCH (mips)
EXTERN (_start)
EXTERN(__init_interrupts)
EXTERN(__init_cop1)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea here is that the compiler will auto-remove constructors of modules that are not used by the application. This is actually a good feature for us, since we want in general to reduce code size.

In this specific case, I would say that the interrupt constructor doesn't need extern: if I manage to do a main that never calls disable/enable interrupts (!), then I would not need init_interrupts as well. The interrupts would stay disabled and that's it.

On the other, I think the issue is that you put the constructor in cop1.c which is a new object file and otherwise unreferenced. If you moved that to something like n64sys.c, there wouldn't be a need for this.

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of not having a new file for this one routine. I'll move it into n64sys.c, I think the EXTERN will still be required to make sure the cop1 bits are set, as I don't think every program is guaranteed to use a symbol from n64sys.c

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think n64sys is always included because it contains get_memory_size which is used by sbrk. But I see the point of this being a little fragile in general, so the EXTERN is probably worth it anyway.

src/cop1.c Outdated
uint32_t fcr31 = C1_FCR31();

/* Set FS bit to allow flashing of denormalized floats */
fcr31 |= C1_FS_BIT;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should elaborate a little bit on this. I personally don't know what means "flashing a denomal", so I'd explain here (or a in a function-level comment) why this is required and how it is good.

volatile float x = 1.0f;
x /= FLT_MAX;

ASSERT(x == 0.0f, "Denormalized float was not flushed to zero");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also a comment here to explain that we're testing that an exception is not generated when a denormal is created.

(Technically, a test that could fail with an exception should trap it to avoid trashing the whole testsuite when it fails, but that's something we don't have helpers for, so don't worry. I'm sure it would be a new rabbit hole...)

@@ -0,0 +1,11 @@
#include <float.h>

void __initalize_cop1();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover?

@@ -0,0 +1,11 @@
#include <float.h>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'd call the file test_cop1 or test_float. Typically a file might contain multiple related tests, so the name should be more generic.

tests/testrom.c Outdated
@@ -202,12 +203,11 @@ static const struct Testsuite
TEST_FUNC(test_eepromfs, 0, TEST_FLAGS_IO),
TEST_FUNC(test_cache_invalidate, 1763, TEST_FLAGS_NONE),
TEST_FUNC(test_debug_sdfs, 0, TEST_FLAGS_NO_BENCHMARK),
TEST_FUNC(test_dma_read_misalign, 7003, TEST_FLAGS_NONE),
TEST_FUNC(test_dma_read_misalign, 7003, TEST_FLAGS_NO_BENCHMARK),
TEST_FUNC(test_denormalized, 0, TEST_FLAGS_NO_EMULATOR),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tab vs space here

tests/testrom.c Outdated
@@ -202,12 +203,11 @@ static const struct Testsuite
TEST_FUNC(test_eepromfs, 0, TEST_FLAGS_IO),
TEST_FUNC(test_cache_invalidate, 1763, TEST_FLAGS_NONE),
TEST_FUNC(test_debug_sdfs, 0, TEST_FLAGS_NO_BENCHMARK),
TEST_FUNC(test_dma_read_misalign, 7003, TEST_FLAGS_NONE),
TEST_FUNC(test_dma_read_misalign, 7003, TEST_FLAGS_NO_BENCHMARK),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please leave this with benchmark? It doesn't fail on many consoles so there must be something else that we should look into. If you change this, we will simply forget about it.

src/interrupt.c Outdated
void init_interrupts()
{
__init_interrupts();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__init_interrupts is idempotent, but since it's been already called by the constructor there's really no need to call it again, I would say. In fact, you could even change this to function to a static inline function in the header file that is empty and thus removed even if compiled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of -Werror, if we define a function and then never use it an error will generated. Perhaps we just make it an macro instead that evaluates to nothing?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this problem in rsp.h for deprecated functions, but anyway you can use __attribute__((used)) to disable that warning.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I was missing the inline part, that seems to have fixed it. I pushed a new commit that should address all of your comments.

Copy link
Collaborator

@rasky rasky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This is good to go IMO

include/cop1.h Outdated
@@ -34,6 +34,8 @@
#define C1_CAUSE_INVALID_OP 0x00010000
#define C1_CAUSE_NOT_IMPLEMENTED 0x00020000

#define C1_FS_BIT (1<<24)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should name this C1_FCR31_FS. I think we usually put the register name first for which the mask must be used.

src/n64sys.c Outdated
is generated by any floating point operations. In order to prevent
this exception we set the FS bit in the fcr31 control register to
instead "flash" and "flush" the denormalized number. To understand
the flashing rules please read pg. 213 of the VR4300 programmers manaul */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo "manual"

src/n64sys.c Outdated
@@ -251,4 +251,27 @@ void wait_ms( unsigned long wait_ms )
wait_ticks(TICKS_FROM_MS(wait_ms));
}


/**
* @brief Initalize COP1 with default settings that prevent undesirable exceptions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo "Initialize"

@Hazematman
Copy link
Contributor Author

Thanks for all the feedback everyone :) I've pushed a new commit that resolves the above mentioned issues

src/n64sys.c Outdated
*/
__attribute__((constructor)) void __init_cop1()
{
/* Read initalized value from cop1 control register */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo "initialized".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! It seems that although english is my first language my proof reading skills are very bad 😅 I've pushed a new commit with that typo fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sp1187 could you review the final patch, and sign off if you think its good?

@anacierdem anacierdem merged commit 2bd68cf into DragonMinded:trunk Oct 13, 2021
@anacierdem anacierdem mentioned this pull request Oct 21, 2021
rasky added a commit to rasky/libdragon that referenced this pull request Nov 2, 2021
In DragonMinded#195, we fixed a bug (off-by-one) in __do_global_ctors that caused
the last constructor not to be called in C programs (using ld linker).

This in turn broke C++ software, that uses g++ as linker. We found out
that ld and g++ creates different constructor tables. We attempted a
first fix in DragonMinded#197 by switching n64.mk to use g++, but this broke the
usage of ld linker; so we fixed it again in DragonMinded#201 by separating the
codebase for ld (__do_global_ctors) vs g++ (__wrap___do_global_ctors).

Unfortunately, in doing so in DragonMinded#201, we reverted to the version of
__do_global_ctors that had the initial bug, effectively reverting DragonMinded#195.
So, all in all, we still have code built by ld broken because the
last constructor (eg: init_interrupts) is not called.

So this PR introduces again the fix in DragonMinded#195, hopefully fixing it finally
for good.
rasky added a commit to rasky/libdragon that referenced this pull request Nov 3, 2021
In DragonMinded#195, we fixed a bug (off-by-one) in __do_global_ctors that caused
the last constructor not to be called in C programs (using ld linker).

This in turn broke C++ software, that uses g++ as linker. We found out
that ld and g++ creates different constructor tables. We attempted a
first fix in DragonMinded#197 by switching n64.mk to use g++, but this broke the
usage of ld linker; so we fixed it again in DragonMinded#201 by separating the
codebase for ld (__do_global_ctors) vs g++ (__wrap___do_global_ctors).

Unfortunately, in doing so in DragonMinded#201, we reverted to the version of
__do_global_ctors that had the initial bug, effectively reverting DragonMinded#195.
So, all in all, we still have code built by ld broken because the
last constructor (eg: init_interrupts) is not called.

So this PR introduces again the fix in DragonMinded#195, hopefully fixing it finally
for good.
rasky added a commit to rasky/libdragon that referenced this pull request Nov 7, 2021
In DragonMinded#195, we fixed a bug (off-by-one) in __do_global_ctors that caused
the last constructor not to be called in C programs (using ld linker).

This in turn broke C++ software, that uses g++ as linker. We found out
that ld and g++ creates different constructor tables. We attempted a
first fix in DragonMinded#197 by switching n64.mk to use g++, but this broke the
usage of ld linker; so we fixed it again in DragonMinded#201 by separating the
codebase for ld (__do_global_ctors) vs g++ (__wrap___do_global_ctors).

Unfortunately, in doing so in DragonMinded#201, we reverted to the version of
__do_global_ctors that had the initial bug, effectively reverting DragonMinded#195.
So, all in all, we still have code built by ld broken because the
last constructor (eg: init_interrupts) is not called.

So this PR introduces again the fix in DragonMinded#195, hopefully fixing it finally
for good.
rasky added a commit that referenced this pull request Nov 7, 2021
In #195, we fixed a bug (off-by-one) in __do_global_ctors that caused
the last constructor not to be called in C programs (using ld linker).

This in turn broke C++ software, that uses g++ as linker. We found out
that ld and g++ creates different constructor tables. We attempted a
first fix in #197 by switching n64.mk to use g++, but this broke the
usage of ld linker; so we fixed it again in #201 by separating the
codebase for ld (__do_global_ctors) vs g++ (__wrap___do_global_ctors).

Unfortunately, in doing so in #201, we reverted to the version of
__do_global_ctors that had the initial bug, effectively reverting #195.
So, all in all, we still have code built by ld broken because the
last constructor (eg: init_interrupts) is not called.

So this PR introduces again the fix in #195, hopefully fixing it finally
for good.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants